feat(slack-app phase 3a): /slack/events webhook + email-match identity#27
Merged
feat(slack-app phase 3a): /slack/events webhook + email-match identity#27
Conversation
Stands up the Slack events endpoint, signature verification, and the identity resolver. The bot now responds to @mentions in channels and DMs with a Phase-3a canned reply confirming who it sees you as. Phase 3b swaps the canned reply for the Anthropic agent loop with memory tools. Backend: - POST /slack/events route: public (signature-verified, not Bearer auth). preParsing hook captures the raw body so the HMAC sees the bytes Slack signed. URL verification handshake echoes the challenge synchronously. event_callback acks 200 immediately and runs the handler as a fire- and-forget Promise (Slack's 3s deadline). - src/slack-api.ts: thin wrappers around users.info, chat.postMessage, chat.postEphemeral. SlackResult discriminated union for clean error handling. - src/slack-identity.ts: resolveSlackUserToReflectUser. One rule: the Slack user's email must lookup-match a Reflect user that's bound to the workspace's team (or the workspace's solo Reflect user). All refusal paths return a clean human-readable reason — no fallback link, no manual mapping UI. - src/slack-events-handler.ts: processSlackEvent (sync filter) + handleUserMessage (async). Skips bot/edit/non-im events, posts ephemeral refusals in channels and regular replies in DMs, records audit slack.message.handled or slack.auth_refused with both Slack user id and resolved Reflect user id. Tests (+12, 347 total): - HTTP integration: url_verification challenge echo, signature gate (missing/wrong/stale), event_callback ack-200 contract. - processSlackEvent unit: ignored event types (bot_message, message_changed/deleted, public-channel non-mention, incomplete payloads, self-app messages). Async handler tests against a real DB+mocked Slack are deferred to Phase 3b — cross-process mocking is fragile; manual smoke against the live Reflect Dev app is faster + more honest. Refs: parent memory d959bc61. Made-with: Cursor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 3a of the Slack app (full design: `docs/eng-plan-slack-app-v1.md`, parent memory `d959bc61`). Stands up the events webhook, signature verification, and the identity resolver. The bot now responds to @mentions in channels and DMs with a canned reply confirming who it sees you as. Phase 3b (next PR) swaps the canned reply for the Anthropic agent loop with memory tools.
What's new
Tests
347/347 backend tests green (was 335; +12). Coverage:
End-to-end async handler tests with a mocked Slack are deferred to Phase 3b. Cross-process mocking (test process vs. server process) is fragile; manual smoke against the live `Reflect Dev` app is faster and more honest at this stage.
Manual step needed AFTER merge + dev deploy
The Slack app's Event Subscriptions are still OFF. After dev deploy lands:
Test plan
Made with Cursor