Skip to content

feat(trogon-gateway): add Slack Socket Mode - #175

Merged
yordis merged 1 commit into
mainfrom
yordis/gateway-slack-secret-ws
May 22, 2026
Merged

feat(trogon-gateway): add Slack Socket Mode#175
yordis merged 1 commit into
mainfrom
yordis/gateway-slack-secret-ws

Conversation

@yordis

@yordis yordis commented May 22, 2026

Copy link
Copy Markdown
Member
  • Enables private and local-first Slack deployments that cannot depend on public webhook ingress.
  • Keeps Slack delivery durable at the gateway boundary while leaving replay and resume semantics with JetStream consumers.
  • Preserves existing webhook deployments while making transport ownership explicit per integration.

@cursor

cursor Bot commented May 22, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Adds a new Slack ingestion path using outbound WebSockets and introduces transport selection/validation per integration, which can affect Slack event delivery and routing if misconfigured. Changes are localized to Slack source/config but touch gateway startup and HTTP route mounting behavior.

Overview
Adds Slack Socket Mode support to trogon-gateway as an alternative to Slack HTTP webhooks, enabling outbound WebSocket delivery while publishing the same Events/interactive/slash-command payloads to NATS.

Slack integrations now require configuring exactly one transport (webhook or socket_mode) via TOML; config loading validates conflicts, introduces an xapp--prefixed app_token, and updates HTTP mounting to only expose Slack routes for webhook-backed integrations.

Gateway startup now spawns a Socket Mode runner for Slack integrations using socket_mode, including reconnect/backoff handling and acking Slack envelopes only after successful durable NATS publish; docs and .env.example are updated accordingly, and tests cover the new transport and routing behavior.

Reviewed by Cursor Bugbot for commit 65cac59. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@yordis has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 15 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 84f0757e-0116-46ac-ab68-ece3494969db

📥 Commits

Reviewing files that changed from the base of the PR and between f77c1b0 and 65cac59.

⛔ Files ignored due to path filters (1)
  • rsworkspace/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • devops/docker/compose/.env.example
  • devops/docker/compose/services/trogon-gateway/README.md
  • devops/docker/compose/services/trogon-gateway/gateway.toml
  • rsworkspace/crates/trogon-gateway/Cargo.toml
  • rsworkspace/crates/trogon-gateway/src/config.rs
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • rsworkspace/crates/trogon-gateway/src/main.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/config.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/mod.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/server.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/socket_mode.rs

Walkthrough

This PR adds Slack Socket Mode transport support as an alternative to HTTP webhooks. It refactors webhook handling into a reusable SlackBridge component, introduces a WebSocket-based event ingestion path with automatic reconnection, and updates configuration to enforce exactly one transport mode per integration.

Changes

Slack Socket Mode Transport Support

Layer / File(s) Summary
Configuration types and dual-transport validation
rsworkspace/crates/trogon-gateway/Cargo.toml, rsworkspace/crates/trogon-gateway/src/config.rs, rsworkspace/crates/trogon-gateway/src/source/slack/config.rs
Adds tokio-tungstenite dependency; introduces SlackAppToken with xapp- prefix validation and SlackAppTokenError; refactors Slack configuration into transport-variant structs (SlackWebhookConfig, SlackSocketModeConfig, SlackTransportConfig); adds SlackTransportConflict error for invalid dual-transport configs; updates config resolution to select exactly one transport per integration with validation helpers and comprehensive test coverage for socket mode, missing tokens, invalid tokens, and transport conflict cases.
HTTP webhook handler refactoring into SlackBridge
rsworkspace/crates/trogon-gateway/src/source/slack/server.rs
Introduces SlackBridge<P, S> struct encapsulating NATS publishing and webhook payload handling (JSON event callbacks, form interactions, slash commands, and unroutable messaging). Updates router wiring to extract SlackWebhookConfig from config early and pass it to router_with_clock; changes AppState to store bridge instead of raw NATS and subject-prefix fields. Updates all webhook request handling to delegate through bridge methods. Adjusts test setup to nest webhook fields under transport config and construct AppState via SlackBridge::new(...).
Socket Mode WebSocket ingestion with reconnection
rsworkspace/crates/trogon-gateway/src/source/slack/socket_mode.rs
Introduces SocketModeError for config, HTTP, websocket, and JSON failures; implements run() as an infinite reconnect loop with exponential backoff; connect_once() to manage WebSocket lifecycle, frame dispatch by envelope kind, and ping/pong handling; open_socket_url() to call Slack apps.connections.open API; handle_payload_envelope() to validate, route events/interactions/slash commands through SlackBridge, and emit ack only on success. Comprehensive test suite covers acking, subject publishing, publish failures, unroutable messages, websocket URL validation, and disconnect frame handling.
Socket mode task spawning
rsworkspace/crates/trogon-gateway/src/main.rs
Updates serve() to spawn a dedicated async task per Slack integration whose socket_mode() is enabled; each task uses tokio::select! to stop on shutdown signal or run the socket-mode runner, reporting task status and errors.
HTTP webhook routing with conditional mounting
rsworkspace/crates/trogon-gateway/src/http.rs
Changes mount_sources() to iterate Slack integrations, conditionally skip those without webhook config, nest routers under /sources/slack/{id}, and log each mounted integration. Adds test verifying socket_mode-only integrations return 404 NOT_FOUND on webhook route.
Module documentation and examples
rsworkspace/crates/trogon-gateway/src/source/slack/mod.rs, devops/docker/compose/services/trogon-gateway/README.md, devops/docker/compose/services/trogon-gateway/gateway.toml, devops/docker/compose/.env.example
Updates Slack module top-level docs to describe both HTTP webhook and Socket Mode transports; declares public socket_mode submodule. README adds "Slack Socket Mode" section explaining single-transport requirement with TOML example and NATS config. Adds commented socket-mode configuration block in gateway.toml with app_token environment wiring. Adds SLACK_PRIMARY_APP_TOKEN placeholder to .env.example.

Sequence Diagram

sequenceDiagram
    participant Config as Configuration
    participant Main as serve()
    participant WS as Socket Mode<br/>Task
    participant Slack as Slack API
    participant Bridge as SlackBridge
    participant NATS

    Config->>Config: validate transport<br/>(webhook XOR socket_mode)
    
    Main->>Main: iterate integrations<br/>with socket_mode enabled
    Main->>WS: spawn async task<br/>per integration
    
    WS->>WS: run() reconnect loop
    WS->>Slack: open_socket_url()<br/>apps.connections.open
    Slack-->>WS: WebSocket url
    WS->>Slack: connect WebSocket
    Slack-->>WS: envelope stream
    
    WS->>WS: parse SocketEnvelope
    WS->>WS: handle_payload_envelope()
    WS->>Bridge: dispatch by kind<br/>(events/interactive/slash)
    Bridge->>NATS: publish event
    NATS-->>Bridge: success
    Bridge-->>WS: StatusCode
    WS->>Slack: send ack frame
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • TrogonStack/trogonai#156: Both PRs modify Slack webhook integration routing under /sources/<source>/<integration.id> with conditional mounting based on integration config.
  • TrogonStack/trogonai#117: Both PRs refactor mount_sources wiring in http.rs to change how integrations' routes are mounted.

Suggested labels

rust:coverage-baseline-reset

Poem

🐰 A socket opens wide, where webhooks once did roam,
Slack events drift gracefully through the async home,
Two transports now coexist, one chosen true,
Reconnecting gently when the connection's through! 🔌✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.19% 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 accurately describes the main feature addition: Slack Socket Mode support for the trogon-gateway service.
Description check ✅ Passed The description is directly related to the changeset, explaining the key benefits and design decisions around Slack Socket Mode implementation.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch yordis/gateway-slack-secret-ws

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.

@github-actions

github-actions Bot commented May 22, 2026

Copy link
Copy Markdown

badge

Code Coverage Summary

Details
Filename                                                                              Stmts    Miss  Cover    Missing
----------------------------------------------------------------------------------  -------  ------  -------  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
crates/trogon-gateway/src/source/gitlab/signature.rs                                    165       0  100.00%
crates/trogon-gateway/src/source/gitlab/gitlab_signing_token.rs                          74       0  100.00%
crates/trogon-gateway/src/source/gitlab/server.rs                                       460       0  100.00%
crates/trogon-decider-runtime/src/stream/append_stream.rs                                 5       0  100.00%
crates/trogon-decider-runtime/src/stream/mod.rs                                          38       0  100.00%
crates/trogon-decider-runtime/src/stream/stream_position.rs                              29       0  100.00%
crates/trogon-decider-runtime/src/stream/read_stream.rs                                  10       0  100.00%
crates/trogon-nats/src/telemetry/messaging.rs                                            82       0  100.00%
crates/mcp-nats/src/server.rs                                                            31       0  100.00%
crates/mcp-nats/src/jsonrpc.rs                                                           22       0  100.00%
crates/mcp-nats/src/mcp_prefix.rs                                                        36       0  100.00%
crates/mcp-nats/src/client.rs                                                            31       0  100.00%
crates/mcp-nats/src/mcp_peer_id.rs                                                       33       0  100.00%
crates/mcp-nats/src/transport.rs                                                        722       0  100.00%
crates/mcp-nats/src/config.rs                                                           110       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/all_client.rs                             6       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/all_server.rs                             6       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/one_server.rs                             9       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/one_client.rs                             9       0  100.00%
crates/trogon-decider-runtime/src/event/codec/event_decode.rs                             3       0  100.00%
crates/trogon-gateway/src/streams.rs                                                    169      10  94.08%   11, 23, 31, 39, 47, 55, 63, 71, 79, 87
crates/trogon-gateway/src/config.rs                                                    2655      46  98.27%   105, 124, 342-343, 346, 744, 747, 907, 964, 1047, 1050, 1053, 1057, 1141-1148, 1225, 1228, 1231, 1236, 1294, 1297, 1300, 1379, 1382, 1385, 1389, 1453, 1456, 1459, 1522, 1525, 1528, 1533, 1608, 1611, 1614, 1619, 1677, 1680, 1683, 1896-1898
crates/trogon-gateway/src/main.rs                                                       116       0  100.00%
crates/trogon-gateway/src/source_integration_id.rs                                       61       3  95.08%   55, 57, 65
crates/trogon-gateway/src/source_status.rs                                               28       0  100.00%
crates/trogon-gateway/src/http.rs                                                       217       1  99.54%   128
crates/trogon-gateway/src/source/standard_webhooks.rs                                   172       0  100.00%
crates/acp-nats/src/client/terminal_output.rs                                           206       0  100.00%
crates/acp-nats/src/client/terminal_release.rs                                          347       0  100.00%
crates/acp-nats/src/client/ext_session_prompt_response.rs                               135       0  100.00%
crates/acp-nats/src/client/fs_write_text_file.rs                                        418       0  100.00%
crates/acp-nats/src/client/fs_read_text_file.rs                                         356       0  100.00%
crates/acp-nats/src/client/terminal_kill.rs                                             290       0  100.00%
crates/acp-nats/src/client/terminal_create.rs                                           274       0  100.00%
crates/acp-nats/src/client/ext.rs                                                       308       8  97.40%   163-172, 189-198
crates/acp-nats/src/client/request_permission.rs                                        308       0  100.00%
crates/acp-nats/src/client/mod.rs                                                      2851       0  100.00%
crates/acp-nats/src/client/terminal_wait_for_exit.rs                                    378       0  100.00%
crates/acp-nats/src/client/session_update.rs                                             55       0  100.00%
crates/acp-nats/src/client/rpc_reply.rs                                                  64       0  100.00%
crates/acp-nats/src/telemetry/metrics.rs                                                 53       0  100.00%
crates/mcp-nats/src/telemetry/transport.rs                                                6       0  100.00%
crates/trogon-nats/src/jetstream/create_conflicts.rs                                     24       0  100.00%
crates/trogon-nats/src/jetstream/publish.rs                                              64       0  100.00%
crates/trogon-nats/src/jetstream/claim_check.rs                                         346       0  100.00%
crates/trogon-nats/src/jetstream/traits.rs                                               40      40  0.00%    181-251
crates/trogon-nats/src/jetstream/mocks.rs                                              1637       7  99.57%   450, 824-826, 1003-1005
crates/trogon-nats/src/jetstream/stream_max_age.rs                                       18       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/fs_write_text_file.rs                       12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_create.rs                          12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_request_permission.rs               12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_wait_for_exit.rs                   12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_kill.rs                            12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_output.rs                          12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/fs_read_text_file.rs                        12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_release.rs                         12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_update.rs                           12       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_client.rs                            15       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_agent.rs                             15       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/global_all.rs                             9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_client.rs                             9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent_ext.rs                          9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_session.rs                            9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/prompt_wildcard.rs                        9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_session.rs                           12       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent.rs                              9       0  100.00%
crates/trogon-gateway/src/source/telegram/server.rs                                     339       0  100.00%
crates/trogon-gateway/src/source/telegram/config.rs                                     109       0  100.00%
crates/trogon-gateway/src/source/telegram/signature.rs                                   32       0  100.00%
crates/trogon-gateway/src/source/telegram/registration.rs                               327       0  100.00%
crates/trogon-decider-runtime/src/event/mod.rs                                          162       0  100.00%
crates/trogon-decider-runtime/src/event/event_id.rs                                      32       0  100.00%
crates/trogon-decider-runtime/src/event/stream_event.rs                                   8       0  100.00%
crates/trogon-decider-runtime/src/event/event_identity.rs                                 3       0  100.00%
crates/trogon-decider-nats/src/stream_store.rs                                          725      84  88.41%   70-213, 240-258, 301, 329-330, 333, 349-353, 519-520, 561, 574-578
crates/trogon-decider-nats/src/snapshot_store.rs                                        740     140  81.08%   64-217, 265-267, 305-313, 322-323, 338-344, 458-473, 502-511, 563, 568, 646-648, 654-656, 688-689, 699-700, 718, 742-743
crates/trogon-decider-nats/src/store.rs                                                 145      65  55.17%   33-187
crates/trogon-gateway/src/source/sentry/signature.rs                                     54       0  100.00%
crates/trogon-gateway/src/source/sentry/server.rs                                       311       0  100.00%
crates/trogon-gateway/src/source/sentry/sentry_client_secret.rs                          17       0  100.00%
crates/acp-nats-agent/src/connection.rs                                                1270       1  99.92%   607
crates/mcp-nats/src/nats/subjects/server/ping.rs                                          9       0  100.00%
crates/mcp-nats/src/nats/subjects/server/complete.rs                                     12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_prompts.rs                                 12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/cancel_task.rs                                  12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_resource_templates.rs                      12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/progress.rs                                     12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/elicitation_completed.rs                        12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/get_prompt.rs                                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/resource_updated.rs                             12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/initialize.rs                                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/call_tool.rs                                    12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/subscribe_resource.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_tools.rs                                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/cancelled.rs                                    12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/resource_list_changed.rs                        12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_tasks.rs                                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_resources.rs                               12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/read_resource.rs                                12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/tool_list_changed.rs                            12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/get_task.rs                                     12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/prompt_list_changed.rs                          12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/unsubscribe_resource.rs                         12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/logging_message.rs                              12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/set_logging_level.rs                            12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/get_task_result.rs                              12       0  100.00%
crates/trogon-std/src/time/system.rs                                                     31       0  100.00%
crates/trogon-std/src/time/mock.rs                                                      125       0  100.00%
crates/acp-nats/src/jetstream/consumers.rs                                               91       0  100.00%
crates/acp-nats/src/jetstream/streams.rs                                                163       4  97.55%   206-208, 218
crates/acp-nats/src/jetstream/ext_policy.rs                                              26       0  100.00%
crates/acp-nats/src/jetstream/provision.rs                                               53       0  100.00%
crates/mcp-nats/src/nats/subjects/mod.rs                                                 89       0  100.00%
crates/trogon-gateway/src/source/microsoft_graph/client_state.rs                         30       0  100.00%
crates/trogon-gateway/src/source/microsoft_graph/server.rs                              325       0  100.00%
crates/trogon-gateway/src/source/linear/server.rs                                       386       0  100.00%
crates/trogon-gateway/src/source/linear/config.rs                                        17       0  100.00%
crates/trogon-gateway/src/source/linear/signature.rs                                     54       1  98.15%   16
crates/acp-nats/src/client_proxy.rs                                                     181       0  100.00%
crates/acp-nats/src/acp_prefix.rs                                                        50       0  100.00%
crates/acp-nats/src/ext_method_name.rs                                                   68       0  100.00%
crates/acp-nats/src/error.rs                                                             82       0  100.00%
crates/acp-nats/src/in_flight_slot_guard.rs                                              32       0  100.00%
crates/acp-nats/src/lib.rs                                                               69       0  100.00%
crates/acp-nats/src/session_id.rs                                                        71       0  100.00%
crates/acp-nats/src/req_id.rs                                                            39       0  100.00%
crates/acp-nats/src/config.rs                                                           203       0  100.00%
crates/acp-nats/src/jsonrpc.rs                                                            6       0  100.00%
crates/acp-nats/src/pending_prompt_waiters.rs                                           134       0  100.00%
crates/trogon-decider/src/testing.rs                                                    660       0  100.00%
crates/trogon-decider/src/decision.rs                                                    37       0  100.00%
crates/trogon-decider/src/events.rs                                                      49       0  100.00%
crates/trogon-decider/src/lib.rs                                                        143       0  100.00%
crates/trogon-decider/src/act.rs                                                         62       0  100.00%
crates/mcp-nats-server/src/config.rs                                                    276       0  100.00%
crates/mcp-nats-server/src/allowed_host.rs                                               90       0  100.00%
crates/mcp-nats-server/src/main.rs                                                      357     127  64.43%   149-166, 202-204, 214, 220-221, 228-231, 255-257, 261-270, 292-305, 310-358, 489, 492, 500-542
crates/trogon-std/src/uuid.rs                                                             7       0  100.00%
crates/trogon-std/src/args.rs                                                            19       9  52.63%   11-28
crates/trogon-std/src/signal.rs                                                          26      12  53.85%   6-11, 18-25, 34
crates/trogon-std/src/duration.rs                                                        45       0  100.00%
crates/trogon-std/src/secret_string.rs                                                   35       0  100.00%
crates/trogon-std/src/json.rs                                                            30       0  100.00%
crates/trogon-std/src/http.rs                                                            19       0  100.00%
crates/acp-nats/src/nats/subjects/stream.rs                                              56       0  100.00%
crates/acp-nats/src/nats/subjects/mod.rs                                                362       0  100.00%
crates/acp-nats-stdio/src/main.rs                                                       135      25  81.48%   65, 113-120, 126-128, 145, 174-193
crates/acp-nats-stdio/src/config.rs                                                      66       0  100.00%
crates/trogon-gateway/src/source/notion/server.rs                                       318       8  97.48%   93-97, 130-131, 150-151
crates/trogon-gateway/src/source/notion/notion_verification_token.rs                     17       0  100.00%
crates/trogon-gateway/src/source/notion/verification_token.rs                           240       0  100.00%
crates/trogon-gateway/src/source/notion/notion_event_type.rs                             46       3  93.48%   47-49
crates/trogon-gateway/src/source/notion/signature.rs                                     56       1  98.21%   32
crates/acp-nats-server/src/connection.rs                                                171      32  81.29%   76-83, 88-99, 115, 117-118, 123, 132-133, 138, 142, 146, 149, 157, 161, 164, 167-171, 207
crates/acp-nats-server/src/config.rs                                                    137       9  93.43%   41, 50-61
crates/acp-nats-server/src/main.rs                                                      896      10  98.88%   100, 231-238, 437
crates/acp-nats-server/src/acp_connection_id.rs                                          45       0  100.00%
crates/acp-nats-server/src/transport.rs                                                1852     110  94.06%   277, 452, 536, 554, 581, 635, 640, 659, 671, 790, 813-815, 867, 884-887, 982-985, 1059, 1062, 1065, 1074, 1078, 1081, 1084-1087, 1106, 1138-1141, 1149-1154, 1166-1170, 1174-1183, 1195-1196, 1214-1215, 1225, 1241-1245, 1273-1279, 1290, 1293-1300, 1305-1309, 1312-1317, 1334, 1336-1337, 1419-1420, 1432-1433, 1453-1454, 1506-1522, 2218, 2261, 2313, 2368, 2380
crates/trogon-service-config/src/lib.rs                                                  92       0  100.00%
crates/trogon-std/src/env/system.rs                                                      17       0  100.00%
crates/trogon-std/src/env/in_memory.rs                                                   73       0  100.00%
crates/mcp-nats/src/nats/mod.rs                                                          99       0  100.00%
crates/mcp-nats/src/nats/parsing.rs                                                     191       0  100.00%
crates/trogon-gateway/src/source/slack/socket_mode.rs                                   731       0  100.00%
crates/trogon-gateway/src/source/slack/signature.rs                                      77       0  100.00%
crates/trogon-gateway/src/source/slack/config.rs                                         68       0  100.00%
crates/trogon-gateway/src/source/slack/server.rs                                        854       0  100.00%
crates/trogon-gateway/src/source/github/signature.rs                                     61       0  100.00%
crates/trogon-gateway/src/source/github/server.rs                                       328       0  100.00%
crates/trogon-gateway/src/source/github/config.rs                                        17       0  100.00%
crates/trogon-gateway/src/source/discord/gateway.rs                                     426       1  99.77%   137
crates/trogon-gateway/src/source/discord/config.rs                                      108       0  100.00%
crates/trogon-decider-runtime/src/snapshot/codec/snapshot_envelope_encode_error.rs       20       0  100.00%
crates/trogon-decider-runtime/src/snapshot/codec/snapshot_encode_error.rs                22       3  86.36%   11-13
crates/trogon-decider-runtime/src/snapshot/codec/encoded_snapshot.rs                     57       0  100.00%
crates/trogon-decider-runtime/src/snapshot/codec/snapshot_payload_decode.rs               3       0  100.00%
crates/trogon-decider-runtime/src/snapshot/codec/snapshot_decode_error.rs                22       3  86.36%   11-13
crates/trogon-decider-runtime/src/snapshot/codec/snapshot_envelope_decode_error.rs       38       0  100.00%
crates/acp-nats/src/agent/logout.rs                                                      49       0  100.00%
crates/acp-nats/src/agent/js_request.rs                                                 283       0  100.00%
crates/acp-nats/src/agent/resume_session.rs                                              90       0  100.00%
crates/acp-nats/src/agent/initialize.rs                                                  79       0  100.00%
crates/acp-nats/src/agent/prompt.rs                                                     471       0  100.00%
crates/acp-nats/src/agent/close_session.rs                                               63       0  100.00%
crates/acp-nats/src/agent/set_session_mode.rs                                            67       0  100.00%
crates/acp-nats/src/agent/authenticate.rs                                                49       0  100.00%
crates/acp-nats/src/agent/set_session_model.rs                                           67       0  100.00%
crates/acp-nats/src/agent/ext_method.rs                                                  82       0  100.00%
crates/acp-nats/src/agent/bridge.rs                                                     123       4  96.75%   108-111
crates/acp-nats/src/agent/test_support.rs                                               267       0  100.00%
crates/acp-nats/src/agent/list_sessions.rs                                               47       0  100.00%
crates/acp-nats/src/agent/set_session_config_option.rs                                   67       0  100.00%
crates/acp-nats/src/agent/cancel.rs                                                     101       0  100.00%
crates/acp-nats/src/agent/ext_notification.rs                                            82       0  100.00%
crates/acp-nats/src/agent/mod.rs                                                         65       0  100.00%
crates/acp-nats/src/agent/fork_session.rs                                                94       0  100.00%
crates/acp-nats/src/agent/new_session.rs                                                 82       0  100.00%
crates/acp-nats/src/agent/load_session.rs                                                89       0  100.00%
crates/acp-nats/src/nats/subjects/commands/close.rs                                      15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/prompt.rs                                     15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/resume.rs                                     15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_config_option.rs                          15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/cancel.rs                                     15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/fork.rs                                       15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_model.rs                                  15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/load.rs                                       15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_mode.rs                                   15       0  100.00%
crates/trogon-decider-runtime/src/execution.rs                                         1202       0  100.00%
crates/trogon-std/src/telemetry/http.rs                                                 217       0  100.00%
crates/acp-nats/src/nats/subjects/global/authenticate.rs                                  6       0  100.00%
crates/acp-nats/src/nats/subjects/global/logout.rs                                        6       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_list.rs                                  6       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_new.rs                                   6       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext_notify.rs                                    9       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext.rs                                           9       0  100.00%
crates/acp-nats/src/nats/subjects/global/initialize.rs                                    6       0  100.00%
crates/trogon-nats/src/token.rs                                                           6       0  100.00%
crates/trogon-nats/src/messaging.rs                                                     561       2  99.64%   144, 154
crates/trogon-nats/src/nats_token.rs                                                    157       0  100.00%
crates/trogon-nats/src/subject_token_violation.rs                                        17       0  100.00%
crates/trogon-nats/src/client.rs                                                         22      22  0.00%    50-86
crates/trogon-nats/src/auth.rs                                                          114       0  100.00%
crates/trogon-nats/src/mocks.rs                                                         317       0  100.00%
crates/trogon-nats/src/connect.rs                                                        94       9  90.43%   22-23, 33, 60-65
crates/mcp-nats-stdio/src/config.rs                                                     160       0  100.00%
crates/mcp-nats-stdio/src/main.rs                                                       212       0  100.00%
crates/trogon-gateway/src/source/twitter/server.rs                                      525       0  100.00%
crates/trogon-gateway/src/source/twitter/config.rs                                       17       0  100.00%
crates/trogon-gateway/src/source/twitter/signature.rs                                    69       0  100.00%
crates/trogon-std/src/fs/mem.rs                                                         216      10  95.37%   61-63, 77-79, 132-134, 157
crates/trogon-std/src/fs/system.rs                                                       92       0  100.00%
crates/trogon-decider-runtime/src/headers/header_value.rs                                37       0  100.00%
crates/trogon-decider-runtime/src/headers/from_entries_error.rs                          11       0  100.00%
crates/trogon-decider-runtime/src/headers/header_map.rs                                  54       3  94.44%   20-22
crates/trogon-decider-runtime/src/headers/mod.rs                                         74       0  100.00%
crates/trogon-decider-runtime/src/headers/header_name.rs                                 33       0  100.00%
crates/trogon-gateway/src/source/incidentio/signature.rs                                206       0  100.00%
crates/trogon-gateway/src/source/incidentio/incidentio_signing_secret.rs                 67       0  100.00%
crates/trogon-gateway/src/source/incidentio/incidentio_event_type.rs                     62       0  100.00%
crates/trogon-gateway/src/source/incidentio/server.rs                                   343       0  100.00%
crates/trogon-gateway/src/source/incidentio/config.rs                                    16       0  100.00%
crates/trogon-telemetry/src/log.rs                                                       68       1  98.53%   33
crates/trogon-telemetry/src/resource_attribute.rs                                        23       0  100.00%
crates/trogon-telemetry/src/service_name.rs                                              44       0  100.00%
crates/trogon-telemetry/src/lib.rs                                                      197      23  88.32%   94, 99, 104, 114-115, 121-139, 175, 178, 181, 187
crates/trogon-telemetry/src/metric.rs                                                    26       1  96.15%   29
crates/trogon-telemetry/src/trace.rs                                                     23       1  95.65%   22
crates/acp-nats/src/nats/subjects/responses/prompt_response.rs                           27       0  100.00%
crates/acp-nats/src/nats/subjects/responses/ext_ready.rs                                 12       0  100.00%
crates/acp-nats/src/nats/subjects/responses/cancelled.rs                                 15       0  100.00%
crates/acp-nats/src/nats/subjects/responses/update.rs                                    27       0  100.00%
crates/acp-nats/src/nats/subjects/responses/response.rs                                  20       0  100.00%
crates/trogon-nats/src/lease/provision.rs                                               187      10  94.65%   82-92
crates/trogon-nats/src/lease/renew.rs                                                   246      19  92.28%   23-29, 48-59
crates/trogon-nats/src/lease/renew_interval.rs                                           61       0  100.00%
crates/trogon-nats/src/lease/lease_key.rs                                                19       0  100.00%
crates/trogon-nats/src/lease/ttl.rs                                                      73       0  100.00%
crates/trogon-nats/src/lease/acquire.rs                                                   5       5  0.00%    9-14
crates/trogon-nats/src/lease/lease_timing.rs                                             15       0  100.00%
crates/trogon-nats/src/lease/release.rs                                                   5       5  0.00%    8-12
crates/trogon-nats/src/lease/nats_kv_lease_config.rs                                     26       0  100.00%
crates/trogon-nats/src/lease/lease_bucket.rs                                             19       0  100.00%
crates/trogon-nats/src/lease/lease_config_error.rs                                       11       0  100.00%
crates/trogon-nats/src/lease/mod.rs                                                     561      13  97.68%   180-193
crates/trogon-std/src/dirs/fixed.rs                                                      80       0  100.00%
crates/trogon-std/src/dirs/system.rs                                                     71       0  100.00%
crates/trogon-decider-runtime/src/snapshot/mod.rs                                         3       0  100.00%
crates/trogon-decider-runtime/src/snapshot/read_snapshot.rs                              11       0  100.00%
crates/mcp-nats/src/nats/subjects/client/initialized.rs                                  12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/ping.rs                                          9       0  100.00%
crates/mcp-nats/src/nats/subjects/client/cancelled.rs                                    12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/create_elicitation.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/create_message.rs                               12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/roots_list_changed.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/list_roots.rs                                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/progress.rs                                     12       0  100.00%
crates/acp-nats/src/nats/mod.rs                                                          23       0  100.00%
crates/acp-nats/src/nats/parsing.rs                                                     278       1  99.64%   151
crates/acp-nats/src/nats/extensions.rs                                                    3       0  100.00%
TOTAL                                                                                 39806     892  97.76%

Diff against main

Filename                                                 Stmts    Miss  Cover
-----------------------------------------------------  -------  ------  --------
crates/trogon-gateway/src/config.rs                        +96      -2  +0.14%
crates/trogon-gateway/src/http.rs                          +25       0  +0.06%
crates/acp-nats-server/src/transport.rs                      0      +4  -0.22%
crates/trogon-gateway/src/source/slack/socket_mode.rs     +731       0  +100.00%
crates/trogon-gateway/src/source/slack/config.rs           +51       0  +100.00%
crates/trogon-gateway/src/source/slack/server.rs            -9       0  +100.00%
TOTAL                                                     +894      +2  +0.05%

Results for commit: 65cac59

Minimum allowed coverage is 95%

♻️ This comment has been updated with latest results

@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.

🧹 Nitpick comments (1)
rsworkspace/crates/trogon-gateway/src/source/slack/socket_mode.rs (1)

125-148: ⚡ Quick win

Redundant JSON parsing on every text frame.

Each text message is parsed twice: once in is_disconnect_frame (line 128) and again in handle_text_frame (line 129). Since handle_text_frame already handles the "disconnect" case (lines 190-196), parse once and consolidate.

♻️ Proposed refactor to parse once
 async fn connect_once<P: JetStreamPublisher, S: ObjectStorePut>(
     client: &reqwest::Client,
     open_url: &str,
     bridge: &SlackBridge<P, S>,
     config: &SlackSocketModeConfig,
 ) -> Result<(), SocketModeError> {
     let websocket_url = open_socket_url(client, open_url, config).await?;
     info!("connecting to Slack Socket Mode");
     let (ws, _) = tokio_tungstenite::connect_async(&websocket_url).await?;
     let (mut sender, mut receiver) = ws.split();

     while let Some(message) = receiver.next().await {
         match message? {
             Message::Text(text) => {
-                let reconnect = is_disconnect_frame(text.as_str())?;
-                if let Some(ack) = handle_text_frame(bridge, text.as_str()).await? {
+                let (reconnect, ack) = handle_text_frame(bridge, text.as_str()).await?;
+                if let Some(ack) = ack {
                     sender.send(Message::Text(ack.into())).await?;
                 }
                 if reconnect {
                     return Ok(());
                 }
             }
             Message::Close(_) => return Ok(()),
             Message::Ping(payload) => sender.send(Message::Pong(payload)).await?,
             Message::Binary(_) | Message::Pong(_) | Message::Frame(_) => {}
         }
     }

     Ok(())
 }

-fn is_disconnect_frame(text: &str) -> Result<bool, SocketModeError> {
-    let envelope: SocketEnvelope = serde_json::from_str(text)?;
-    Ok(envelope.kind == "disconnect")
-}

Then update handle_text_frame to return (bool, Option<String>) where the bool indicates disconnect:

 async fn handle_text_frame<P: JetStreamPublisher, S: ObjectStorePut>(
     bridge: &SlackBridge<P, S>,
     text: &str,
-) -> Result<Option<String>, SocketModeError> {
+) -> Result<(bool, Option<String>), SocketModeError> {
     let envelope: SocketEnvelope = serde_json::from_str(text)?;

     match envelope.kind.as_str() {
         "hello" => {
             info!("Slack Socket Mode connection established");
-            Ok(None)
+            Ok((false, None))
         }
         "disconnect" => {
             warn!(
                 reason = envelope.reason.as_deref().unwrap_or("unknown"),
                 "Slack Socket Mode disconnect requested"
             );
-            Ok(None)
+            Ok((true, None))
         }
-        "events_api" | "interactive" | "slash_commands" => handle_payload_envelope(bridge, envelope, text).await,
+        "events_api" | "interactive" | "slash_commands" => {
+            handle_payload_envelope(bridge, envelope, text).await.map(|ack| (false, ack))
+        }
         other => {
             warn!(kind = other, "Unhandled Slack Socket Mode envelope type");
             bridge
                 .publish_socket_unroutable("unhandled_socket_mode_type", Bytes::copy_from_slice(text.as_bytes()))
                 .await;
-            Ok(envelope.envelope_id.map(ack_frame))
+            Ok((false, envelope.envelope_id.map(ack_frame)))
         }
     }
 }
🤖 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 `@rsworkspace/crates/trogon-gateway/src/source/slack/socket_mode.rs` around
lines 125 - 148, Parse the incoming text frame only once by removing the
separate is_disconnect_frame call: change handle_text_frame to parse the JSON
envelope and return a tuple like (bool, Option<String>) where the bool indicates
a disconnect; update the message loop (the while let Some(message)
receiver.next() block) to call handle_text_frame(text) once, send the returned
ack if Some, and use the returned bool to trigger reconnect/return Ok(()); you
can then delete or deprecate is_disconnect_frame (and ensure SocketEnvelope
deserialization stays in handle_text_frame).
🤖 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.

Nitpick comments:
In `@rsworkspace/crates/trogon-gateway/src/source/slack/socket_mode.rs`:
- Around line 125-148: Parse the incoming text frame only once by removing the
separate is_disconnect_frame call: change handle_text_frame to parse the JSON
envelope and return a tuple like (bool, Option<String>) where the bool indicates
a disconnect; update the message loop (the while let Some(message)
receiver.next() block) to call handle_text_frame(text) once, send the returned
ack if Some, and use the returned bool to trigger reconnect/return Ok(()); you
can then delete or deprecate is_disconnect_frame (and ensure SocketEnvelope
deserialization stays in handle_text_frame).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5fff5790-dc3b-4f33-8226-75a4146b7b86

📥 Commits

Reviewing files that changed from the base of the PR and between bc2c441 and f77c1b0.

⛔ Files ignored due to path filters (1)
  • rsworkspace/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • devops/docker/compose/.env.example
  • devops/docker/compose/services/trogon-gateway/README.md
  • devops/docker/compose/services/trogon-gateway/gateway.toml
  • rsworkspace/crates/trogon-gateway/Cargo.toml
  • rsworkspace/crates/trogon-gateway/src/config.rs
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • rsworkspace/crates/trogon-gateway/src/main.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/config.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/mod.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/server.rs
  • rsworkspace/crates/trogon-gateway/src/source/slack/socket_mode.rs

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f77c1b0. Configure here.

Comment thread rsworkspace/crates/trogon-gateway/src/source/slack/socket_mode.rs
@yordis
yordis force-pushed the yordis/gateway-slack-secret-ws branch from f77c1b0 to ab434b2 Compare May 22, 2026 04:42
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis
yordis force-pushed the yordis/gateway-slack-secret-ws branch from ab434b2 to 65cac59 Compare May 22, 2026 04:48
@yordis yordis added the rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline label May 22, 2026
@yordis
yordis merged commit d0cf944 into main May 22, 2026
9 of 10 checks passed
@yordis
yordis deleted the yordis/gateway-slack-secret-ws branch May 22, 2026 05:40
yordis added a commit that referenced this pull request Jun 16, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 22, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 22, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 23, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 23, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 23, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 25, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 25, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis added a commit that referenced this pull request Jun 26, 2026
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust:coverage-baseline-reset Relax Rust coverage gate to establish a new baseline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant