Skip to content

Fix Slack DM integration event delivery#116

Merged
khaliqgant merged 1 commit into
mainfrom
fix-slack-dm-integration-events
Jun 5, 2026
Merged

Fix Slack DM integration event delivery#116
khaliqgant merged 1 commit into
mainfrom
fix-slack-dm-integration-events

Conversation

@kjgbot

@kjgbot kjgbot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • convert Pear remote subscription filters to Relayfile SDK-compatible path globs so Slack DM channel IDs like D0B... are no longer dropped before Pear sees them
  • keep Pear-side precise filtering for selected Slack channels/DMs, while using websocket transport and legacy catch-up replay with local replay suppression
  • harden Slack raw/slug duplicate handling, edit delivery, concise Slack injection text, explicit-agent delivery, and integration-update idempotency

Root Cause

Pear subscribed to DM events with /slack/channels/D*/**, but the Relayfile SDK transport filter only supports * as a whole segment plus trailing **. The SDK therefore treated D* as a literal segment and filtered out /slack/channels/D0B... events before Pear's own matcher could inject them.

Tests

  • npm test

CodeAnt-AI Description

Fix Slack event delivery and make integration updates more reliable

What Changed

  • Slack channel and DM events now match the selected channel paths correctly, including raw channel IDs and their slugged copies
  • Slack message notifications now show a clearer event summary with the channel, author, and message text when available
  • If a Slack preview cannot be read from the file store, the system now falls back to expanded event data instead of dropping the context
  • Duplicate Slack messages are suppressed by logical message identity, so the same message does not inject twice when it appears in both raw and slugged paths
  • Failed or recipient-less deliveries no longer block a later retry of the same event
  • Integration update messages are no longer held until delivery confirmation and are not re-sent when the content has not changed

Impact

✅ Fewer missing Slack DM notifications
✅ No duplicate Slack message alerts from alias paths
✅ Clearer Slack event context in agent messages

💡 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:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

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:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

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.

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@codeant-ai

codeant-ai Bot commented Jun 5, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Worried about impact? Review this PR in Change Stack to explore blast radius before you approve or request changes.

Review Change Stack

📝 Walkthrough

Walkthrough

This PR enhances Slack event integration by adding content-aware deduplication with logical change fingerprinting, refactoring event context preview generation with Slack-specific extraction, and implementing specialized Slack message formatting. It also refactors system message deduplication from TTL-based to content-based tracking and reorders local mount daemon synchronization.

Changes

Slack Integration Event Handling and System Message Deduplication

Layer / File(s) Summary
Slack path canonicalization and SDK path filter export
src/main/integration-event-bridge.ts (imports, constants, helpers), src/main/__tests__/integration-event-bridge.test.ts (path filter tests)
Introduces slackCanonicalChannelAliases to normalize Slack channel variants and exports relayfileSdkPathFiltersFor() to convert change-path globs into Relayfile-SDK path filters. Defines REMOTE_SUBSCRIPTION_FROM and Slack-specific TTL constants.
Remote subscription configuration with filtered watches and legacy replay
src/main/integration-event-bridge.ts (createWorkspaceScopedEventClient, RelayFileSync), src/main/__tests__/integration-event-bridge.test.ts (subscription tests)
Computes relayfile SDK path filters from path scope or configured globs, configures RelayFileSync with websocket transport when baseUrl is present, and sets subscription replay mode to legacy for persistent catch-up behavior instead of now.
Slack logical change fingerprinting and content-aware dedupe keys
src/main/integration-event-bridge.ts (fingerprinting, hashing, dedupe construction)
Implements slackLogicalChangeFingerprint() that ignores per-file revision differences, stableContentFingerprint() for content hashing, and eventDedupeKeyWithFingerprint() to construct dedupe keys with Slack-specific short TTLs using targeted preview content when available.
Event context preview generation and Slack preview extraction
src/main/integration-event-bridge.ts (preview helpers, refactored flows), src/main/__tests__/integration-event-bridge.test.ts (failReadFile harness, fallback tests)
Refactors preview generation through eventContextPreviewFromBuffer() and eventContextPreviewFromData() helpers. Adds previewRecord(), slackPreviewText(), and slackPreviewAuthor() to safely parse JSON Slack payloads and extract structured text/author fields with fallback to raw content.
Slack integration event message formatting
src/main/integration-event-bridge.ts (Slack message formatting), src/main/__tests__/integration-event-bridge.test.ts (message content, wording, path tests)
Adds slackScopeLabel() and formatSlackIntegrationEventMessage() to generate specialized Slack event messages with channel/DM/user scope labels and context skip reasons. Updates assertions to verify new message format and remove obsolete "Relayfile path" wording.
Event injection with content-aware dedupe, key release, and error handling
src/main/integration-event-bridge.ts (injectEvent dedupe flow, delivery loop), src/main/__tests__/integration-event-bridge.test.ts (alias dedupe, failure/no-recipient key release, notifyAgents tests)
Implements injection flow that computes Slack content-aware dedupe after preview retrieval, releases dedupe keys on send failure or when no recipients exist (enabling retries), and tracks per-recipient delivery errors with aggregated warning emission on partial failures.
Workspace client configuration and test process detection
src/main/integration-event-bridge.ts (test detection, baseUrl passing)
Improves test process detection via NODE_TEST_CONTEXT environment variable and --test in execArgv. Passes relayfile URL as baseUrl to createWorkspaceScopedEventClient() for transport selection.
Content-based system message deduplication
src/main/integrations.ts, src/main/cloud-agent.ts, src/main/integrations.test.ts (dedup tracking, sending, tests)
Removes TTL-based SYSTEM_MESSAGE_DEDUPE_MS and replaces with per-project lastBroadcastSystemMessages map tracking sent content. Records dedupe after send acceptance via broker.sendMessage() instead of preflight. Simplifies CloudAgentManager to always use sendMessage() without waiting.
Local mount daemon synchronization and subscription retry
src/main/integrations.ts, src/main/integrations.test.ts (daemon ordering, retry tests)
Ensures syncLocalMounts() completes before syncActiveEventSubscriptions() is triggered, logging errors instead of using standalone promise rejection. Enables subscription retries after local mount hydration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A Slack event hops through dedupe streams,
Content-aware fingerprints in canonical schemes,
Previews extracted, messages made bright,
Key releases when failures don't bite,
Local mounts sync before subscriptions take flight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix Slack DM integration event delivery' directly corresponds to the primary change addressing Slack DM/channel event filtering and delivery issues.
Description check ✅ Passed The description comprehensively covers the root cause, changes, and impact of fixing Slack DM integration event delivery with clear sections on what changed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-slack-dm-integration-events

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Jun 5, 2026
@agent-relay-code

Copy link
Copy Markdown
Contributor

Reviewed PR #116 against the checked-out diff and traced the changed integration-event and integration guidance paths. I did not make code changes because the current checkout did not present a reproducible defect.

Verified:

  • node --experimental-strip-types --no-warnings --test src/main/__tests__/integration-event-bridge.test.ts
  • npx vitest run src/main/integrations.test.ts
  • npm run build
  • npm test

All passed.

@agent-relay-code

Copy link
Copy Markdown
Contributor

ℹ️ 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 #116 against the checked-out diff and traced the changed integration-event and integration guidance paths. I did not make code changes because the current checkout did not present a reproducible defect.

Verified:

  • node --experimental-strip-types --no-warnings --test src/main/__tests__/integration-event-bridge.test.ts
  • npx vitest run src/main/integrations.test.ts
  • npm run build
  • npm test

All passed.

Comment thread src/main/integrations.ts
Comment on lines +940 to +944
void this.syncLocalMounts()
.then(() => this.syncActiveEventSubscriptions())
.catch((error) => {
console.warn('[integrations] Failed to start local integration mounts:', toErrorMessage(error))
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This fire-and-forget startup chain can continue running after shutdown starts, and the trailing syncActiveEventSubscriptions() can re-open subscriptions that shutdownLocalMounts() just closed. Track/cancel startup work (or await it under lifecycle control) so shutdown cannot race with a late re-subscribe. [race condition]

Severity Level: Major ⚠️
- ⚠️ Event subscriptions can briefly reopen during app shutdown.
- ⚠️ Integration websocket/file watchers may linger after cleanup.
- ⚠️ Shutdown ordering becomes non-deterministic and harder to reason.
Steps of Reproduction ✅
1. On app startup, `src/main/index.ts:329-331` calls `void
integrationsManager.startLocalMountDaemon().catch(...)`, starting background integration
initialization without awaiting it.

2. `startLocalMountDaemon()` at `src/main/integrations.ts:938-945` first awaits
`this.syncActiveEventSubscriptions()` (line 939), then kicks off
`this.syncLocalMounts().then(() => this.syncActiveEventSubscriptions())` as a
fire-and-forget chain (lines 940-942).

3. If the user initiates app shutdown while `syncLocalMounts()` is still running (e.g.
soon after launch while mounts or cloud hydration at `integrations.ts:1885-1949` are in
progress), the shutdown handler in `src/main/index.ts:350-368` calls
`integrationsManager.shutdownLocalMounts()` as part of `Promise.allSettled([...])`.

4. `shutdownLocalMounts()` at `src/main/integrations.ts:955-969` clears system-message
timers and then awaits both `integrationMountManager.stop()` and
`integrationEventBridge.closeAll()`, closing all integration event subscriptions.

5. After `shutdownLocalMounts()` resolves, the original `syncLocalMounts()` call (started
in step 2) may complete; its `.then(() => this.syncActiveEventSubscriptions())` callback
(lines 940-942) then runs even though shutdown is in progress or complete.

6. That late `syncActiveEventSubscriptions()` call at `integrations.ts:1876-1883` reads
the still-set `activeProjectId` from the store and invokes
`syncEventSubscriptions(activeProjectId)` (lines 1877-1882), which performs
`integrationEventBridge.closeAllExcept(projectId)` and
`integrationEventBridge.reconcile(projectId, ...)` at `integrations.ts:1858-1868`.

7. As a result, integration event subscriptions may be re-opened after
`shutdownLocalMounts()` has already closed them, purely due to this untracked
fire-and-forget startup chain racing with the shutdown lifecycle.

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:** 940:944
**Comment:**
	*Race Condition: This fire-and-forget startup chain can continue running after shutdown starts, and the trailing `syncActiveEventSubscriptions()` can re-open subscriptions that `shutdownLocalMounts()` just closed. Track/cancel startup work (or await it under lifecycle control) so shutdown cannot race with a late re-subscribe.

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
👍 | 👎

Comment thread src/main/integrations.ts
if (expiresAt <= now) this.recentlyInjectedSystemMessages.delete(key)
}
if (this.recentlyInjectedSystemMessages.has(messageKey)) return
if (this.lastBroadcastSystemMessages.get(projectId) === message) return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The dedupe check is done before looking up current recipients, so once a message body has been sent once for a project, later calls skip delivery entirely even if a different/new agent is now online. Move dedupe tracking to be recipient-aware (or at least re-evaluate agent set before short-circuiting) so newly joined agents still receive the current integrations update. [logic error]

Severity Level: Major ⚠️
- ⚠️ New broker sessions miss `<integrations-update>` system messages.
- ⚠️ Newly spawned agents may lack current integration context.
- ⚠️ Integrations guidance diverges from actual connected integrations.
Steps of Reproduction ✅
1. App startup registers IPC handler `broker:start` at `src/main/ipc-handlers.ts:58-72`,
which on success calls `integrationsManager.notifyAgentState(projectId)` (lines 65-69).

2. Renderer triggers a broker start for a project (e.g. user starts the local assistant),
invoking `broker:start`, which calls `notifyAgentState()` at
`src/main/integrations.ts:947-949`.

3. `notifyAgentState()` calls `syncAgentState(projectId, true, { waitForAgent: true })`
(`integrations.ts:946-950`), which in turn calls `scheduleSystemMessage()` at
`integrations.ts:1655-1659`. After debounce, `scheduleSystemMessage()` calls
`safeInjectSystemMessage(projectId, message, options)` at `integrations.ts:1841-1844`.

4. On the first run, `safeInjectSystemMessage()` at `integrations.ts:1794-1827` sends the
`<integrations-update>` message to all current agents and then records
`this.lastBroadcastSystemMessages.set(projectId, message)` at line 1827.

5. Later, the user restarts the broker for the same project with unchanged integrations
(same `buildSystemMessageSnippet` output), so `broker:start` again invokes
`notifyAgentState()``scheduleSystemMessage()``safeInjectSystemMessage()`.

6. On this second run, `safeInjectSystemMessage()` hits the dedupe guard at
`integrations.ts:1800` (`if (this.lastBroadcastSystemMessages.get(projectId) === message)
return`) and exits before calling `listSystemMessageAgents()` or `bridge.sendMessage()`,
so any newly-started agents for the project never receive the current integrations-update
message.

7. `lastBroadcastSystemMessages` is never cleared or made recipient-aware (verified via
`grep` at `integrations.ts:722-725` and `integrations.ts:1827` being the only writes), so
this suppression persists for the lifetime of the main process as long as the message body
is unchanged.

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:** 1800:1800
**Comment:**
	*Logic Error: The dedupe check is done before looking up current recipients, so once a message body has been sent once for a project, later calls skip delivery entirely even if a different/new agent is now online. Move dedupe tracking to be recipient-aware (or at least re-evaluate agent set before short-circuiting) so newly joined agents still receive the current integrations update.

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
👍 | 👎

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/integrations.test.ts`:
- Around line 486-498: The mock call history is cleared with
mock.brokerManager.sendMessage.mockClear(), so subsequent assertions should
expect zero new sends; update the two assertions after calls to
manager.notifyAgentState('project-1') (the expectations using
integrationsUpdateSends()) to expect 0 instead of 1 to reflect dedupe working,
keeping the notifyAgentState and vi.advanceTimersByTimeAsync calls unchanged.
- Around line 582-586: The test races because it asserts
integrationEventBridge.reconcile call counts immediately after waiting for
integrationMountManager.ensureMounted; change the test to waitFor the reconcile
behavior instead: use vi.waitFor(() =>
expect(mock.integrationEventBridge.reconcile).toHaveBeenCalledTimes(2)) (or
waitFor the specific toHaveBeenLastCalledWith) before asserting
expect(mock.integrationEventBridge.closeAllExcept).toHaveBeenCalledTimes(2) and
the last-call arguments; this ensures integrationEventBridge.reconcile (and its
chained async step) has completed before making deterministic assertions.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 90b58355-6b16-4d0e-8d72-69f52fed27ae

📥 Commits

Reviewing files that changed from the base of the PR and between 9e04125 and 0ffe166.

📒 Files selected for processing (5)
  • src/main/__tests__/integration-event-bridge.test.ts
  • src/main/cloud-agent.ts
  • src/main/integration-event-bridge.ts
  • src/main/integrations.test.ts
  • src/main/integrations.ts

Comment on lines +486 to +498
mock.brokerManager.sendMessage.mockClear()

// A later reconcile with identical integration state must not re-broadcast
// regardless of elapsed time — duplicate <integrations-update> messages
// were observed minutes apart with the old 30s text-TTL dedupe.
await manager.notifyAgentState('project-1')
await vi.advanceTimersByTimeAsync(1_000)
expect(integrationsUpdateSends()).toBe(1)

await vi.advanceTimersByTimeAsync(60_000)
await manager.notifyAgentState('project-1')
await vi.advanceTimersByTimeAsync(1_000)
expect(integrationsUpdateSends()).toBe(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix dedupe assertions after clearing the mock call history.

After mock.brokerManager.sendMessage.mockClear() on Line 486, the assertions on Lines 493 and 498 should expect 0 new sends (dedupe working), not 1.

Suggested fix
-    expect(integrationsUpdateSends()).toBe(1)
+    expect(integrationsUpdateSends()).toBe(0)
@@
-    expect(integrationsUpdateSends()).toBe(1)
+    expect(integrationsUpdateSends()).toBe(0)
📝 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.

Suggested change
mock.brokerManager.sendMessage.mockClear()
// A later reconcile with identical integration state must not re-broadcast
// regardless of elapsed time — duplicate <integrations-update> messages
// were observed minutes apart with the old 30s text-TTL dedupe.
await manager.notifyAgentState('project-1')
await vi.advanceTimersByTimeAsync(1_000)
expect(integrationsUpdateSends()).toBe(1)
await vi.advanceTimersByTimeAsync(60_000)
await manager.notifyAgentState('project-1')
await vi.advanceTimersByTimeAsync(1_000)
expect(integrationsUpdateSends()).toBe(1)
mock.brokerManager.sendMessage.mockClear()
// A later reconcile with identical integration state must not re-broadcast
// regardless of elapsed time — duplicate <integrations-update> messages
// were observed minutes apart with the old 30s text-TTL dedupe.
await manager.notifyAgentState('project-1')
await vi.advanceTimersByTimeAsync(1_000)
expect(integrationsUpdateSends()).toBe(0)
await vi.advanceTimersByTimeAsync(60_000)
await manager.notifyAgentState('project-1')
await vi.advanceTimersByTimeAsync(1_000)
expect(integrationsUpdateSends()).toBe(0)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/integrations.test.ts` around lines 486 - 498, The mock call history
is cleared with mock.brokerManager.sendMessage.mockClear(), so subsequent
assertions should expect zero new sends; update the two assertions after calls
to manager.notifyAgentState('project-1') (the expectations using
integrationsUpdateSends()) to expect 0 instead of 1 to reflect dedupe working,
keeping the notifyAgentState and vi.advanceTimersByTimeAsync calls unchanged.

Comment on lines +582 to +586
await vi.waitFor(() => expect(mock.integrationMountManager.ensureMounted).toHaveBeenCalled())

expect(mock.integrationEventBridge.closeAllExcept).toHaveBeenCalledTimes(2)
expect(mock.integrationEventBridge.reconcile).toHaveBeenCalledTimes(2)
expect(mock.integrationEventBridge.reconcile).toHaveBeenLastCalledWith(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make the startup retry assertion deterministic.

The test waits for ensureMounted but immediately asserts two reconcile calls. Since the second reconcile runs in a chained async step, this can intermittently race.

Suggested fix
-    await vi.waitFor(() => expect(mock.integrationMountManager.ensureMounted).toHaveBeenCalled())
-
-    expect(mock.integrationEventBridge.closeAllExcept).toHaveBeenCalledTimes(2)
-    expect(mock.integrationEventBridge.reconcile).toHaveBeenCalledTimes(2)
+    await vi.waitFor(() => expect(mock.integrationMountManager.ensureMounted).toHaveBeenCalled())
+    await vi.waitFor(() => expect(mock.integrationEventBridge.closeAllExcept).toHaveBeenCalledTimes(2))
+    await vi.waitFor(() => expect(mock.integrationEventBridge.reconcile).toHaveBeenCalledTimes(2))
📝 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.

Suggested change
await vi.waitFor(() => expect(mock.integrationMountManager.ensureMounted).toHaveBeenCalled())
expect(mock.integrationEventBridge.closeAllExcept).toHaveBeenCalledTimes(2)
expect(mock.integrationEventBridge.reconcile).toHaveBeenCalledTimes(2)
expect(mock.integrationEventBridge.reconcile).toHaveBeenLastCalledWith(
await vi.waitFor(() => expect(mock.integrationMountManager.ensureMounted).toHaveBeenCalled())
await vi.waitFor(() => expect(mock.integrationEventBridge.closeAllExcept).toHaveBeenCalledTimes(2))
await vi.waitFor(() => expect(mock.integrationEventBridge.reconcile).toHaveBeenCalledTimes(2))
expect(mock.integrationEventBridge.reconcile).toHaveBeenLastCalledWith(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/integrations.test.ts` around lines 582 - 586, The test races because
it asserts integrationEventBridge.reconcile call counts immediately after
waiting for integrationMountManager.ensureMounted; change the test to waitFor
the reconcile behavior instead: use vi.waitFor(() =>
expect(mock.integrationEventBridge.reconcile).toHaveBeenCalledTimes(2)) (or
waitFor the specific toHaveBeenLastCalledWith) before asserting
expect(mock.integrationEventBridge.closeAllExcept).toHaveBeenCalledTimes(2) and
the last-call arguments; this ensures integrationEventBridge.reconcile (and its
chained async step) has completed before making deterministic assertions.

@codeant-ai

codeant-ai Bot commented Jun 5, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

@khaliqgant khaliqgant merged commit b9c5d20 into main Jun 5, 2026
4 checks passed
@khaliqgant khaliqgant deleted the fix-slack-dm-integration-events branch June 5, 2026 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants