fix(slack): Enterprise Grid org-wide installs, authorizations[] routing, retry marker (#268) - #277
Conversation
…ng, retry marker (#268) Ports the non-cache half of vercel/chat 907450d7 (#724, chat@4.35.0): - handle_oauth_callback keys org-wide installs by enterprise.id and records enterprise_id / is_enterprise_install - _resolve_event_request_context prefers authorizations[0], shared by HTTP and Socket Mode; socket slash/interactive resolve org-wide installs - _with_token_kwargs injects team_id (org-wide) and client_context_team_id (originating channel) at upstream's withToken call sites; the #95 chat_stream team_id is unchanged - slack:event-delivered:{event_id} retry marker (24 h TTL) - W-prefixed user ids; with_bot_token(installation_id=) Part of #184.
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 6 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (8)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…te (#268) Each _with_token_kwargs call site now has a case asserting team_id (and client_context_team_id on the originating channel) under an org-wide context; a caller-set client_context_team_id is kept. Mutation-checked: dropping the wrap at any one of the 30 request-time sites fails a test.
|
Merge gate: CI green (test 3.12, test 3.13, Lint & Type Check, CodeQL / Analyze actions+python); local Codex review (gpt-6-astra, xhigh, --base origin/main) on 4200922: "No actionable regressions found in the diff against the specified merge base. All 1,234 Slack tests passed, and git diff --check reported no issues."; 1 astra round on the final HEAD (origin/main already an ancestor, no further merge needed); bots: CodeRabbit rate-limited (no review comments), no gemini comments. Merging with --admin (Protect Main requires a code-owner approval). |
Summary
Ports the Enterprise Grid half of #213 (PR B), which is the non-cache part of upstream
907450d7(vercel/chat#724, chat@4.35.0). The installation-scoped caches already landed in #205.handle_oauth_callbacknow keys an org-wide install (is_enterprise_install,team: null) byenterprise.id. If that id is missing it raisesmissing access_token or enterprise.id. The result gainsenterprise_idandis_enterprise_install, andteam_idis always the storage key.SlackInstallationgainsenterprise_id/is_enterprise_install, which are stored asenterpriseId/isEnterpriseInstallonly when set, so a plain install is stored exactly as before._resolve_event_request_context(payload)returns aRequestContext,"not-applicable"or"unresolved". The HTTP and Socket Modeevents_apipaths both use it. It prefersauthorizations[0](flag via??, ids via||) over the top-level fields. It recordsteam_id,context_team_idandcontext_channelonRequestContext._run_slash_commandon HTTP and on the socket. On the socket, JSON booleans become"true"/"false"._extract_installation_from_interactive_payload.events_apienvelope now keepsauthorizations,context_team_id, the enterprise fields andis_ext_shared_channel, as upstream does._with_token_kwargs(**kwargs)is the port of upstreamwithToken. Under an org-wide context it addsteam_id. On calls to the event's channel it addsclient_context_team_id. A key the caller set always wins. It is applied at the same 31 call sites where upstream useswithToken. As upstream,chat_stream, schedule/delete-scheduled,files_upload_v2andoauth_v2_accessare not wrapped._process_event_payloadwritesslack:event-delivered:{event_id}with a 24 h TTL. The write is fire-and-forget: the task is pinned and failures are logged at debug._is_duplicate_event_delivery(payload, retry_num)reads the marker only whenretry_num > 0. It runs on HTTP (x-slack-retry-num), on the live socket (retry_attempt) and on forwarded socket events (retryNum). If the state read fails, the event is processed.SLACK_USER_ID_EXACT_PATTERN = ^[UW][A-Z0-9]+$.with_bot_tokenandwith_bot_token_asynctake a keyword-onlyinstallation_id=None.Upstream commits mapped
907450d7fix(slack): Enterprise Grid support (#724), chat@4.35.0: the non-cache half. The cache half went in with [4.41/SL0] Slack: installation-scoped caches, drop unresolved installs, strict response_url, bounded regexes #205.Tests ported (
tests/test_slack_enterprise_grid.pyunless noted)handleOAuthCallback: keys org-wide installs by enterprise ID; records the enterprise ID on workspace installs within a Grid org; throws when the org-wide response is missingenterprise.id; org-wide OAuth install round-trips with org-wide event webhooks.socket mode - multi-workspace token resolution: all 5 PR B cases. "drops socket interactive payloads with no installation" was already ported in [4.41/SL0] Slack: installation-scoped caches, drop unresolved installs, strict response_url, bounded regexes #205.withToken enterprise context injection: all 7.event delivery deduplication: all 4.W-prefixed enterprise user IDs: both.event routing via authorizations[]: all 3.withBotTokencache scoping: both, intests/test_slack_webhook.py::TestInstallationScopedCaches. They usewith_bot_token_async, because a coroutine returned through the sync form runs after the context is reset.tests/test_slack_api.py::TestStream::test_stream_keeps_the_recipient_team_id_under_an_org_wide_context.post_message/fetch_channel_infocarryteam_id/client_context_team_id."false"string flag.retryNumdedupe.(id, False).is_ext_shared_channel" rule, plus_external_channelspopulation.Mutation check: disabling the marker,
authorizations[0],_with_token_kwargs, the strict flag or the[UW]pattern each makes the matching tests fail.Fidelity
Upstream Slack test files are not fidelity-mapped, so the target report does not change and
scripts/fidelity_target.jsonis unchanged:(Regenerated after merging
origin/mainat5b6bc22:TARGET TOTAL: 964/1064 matched.)Strict:
TOTAL: 733/733 matched (100%), 0 missing, 3 absorbers.Divergences (recorded in
docs/UPSTREAM_SYNC.md, new section "Slack Enterprise Grid…")_on_socket_requeststill skipsretry_attempt > 0(that line belongs to [4.41/SL4b] Slack inbound mrkdwn normalization, channel.post thread ids, Socket Mode retries (remainder of #209) #283, split from [4.41/SL4] Slack inbound normalization: self-mention decode, content is_mention, mrkdwn fixes, bot author ids, socket retries #209). The marker therefore covers HTTP retries and forwarded socket events now. Once [4.41/SL4b] Slack inbound mrkdwn normalization, channel.post thread ids, Socket Mode retries (remainder of #209) #283 removes the skip,retry_attemptalready reaches_route_socket_event(retry_num=...).is_enterprise_installcounts only asTrue/"true"on every path. Upstream's event path usesBoolean(...). This only differs for malformed payloads.chat_streamis not wrapped, as upstream'schatStream. So under an org-wide context the stream keepsteam_id = recipient_team_idand gets noclient_context_team_id.Consumer impact
missing access_token or team.id.team_id, and Slack Connect events echoclient_context_team_idto their channel.authorizations[0], so Slack Connect events resolve to the receiving installation.Merge with main (#207 / #208 / #209 part a)
set_suggested_promptsnow sends throughclient.api_call(json=...)(main). Thejsonbody goes through_with_token_kwargs, as upstream'ssetSuggestedPrompts(await this.withToken({...})).test_api_calls_carry_the_resolved_enterprise_contextnow also covers this path.set_assistant_statusandstart_typingkeep main's loading-messages fallback and are still wrapped in_with_token_kwargs.team_idonchat_streamis unchanged. Every rotated segment ([4.41/SL3] Slack: rotate long native streams before expiry #208) gets it.Validation
After merging
origin/main(through #201/#297,5b6bc22): ruff check/format,audit_test_quality.py(0 hard failures),--check-docs, strict fidelity 733/733, pytest 7777 passed / 24 skipped, pyrefly 0 errors.Merge gate
Independent review findings
_with_token_kwargscall sites had wiring tests, so a dropped wrap would silently loseteam_id/client_context_team_idunder org-wide installs (upstream'swithTokenalso supplies the token, so a skipped site fails loudly there).test_api_calls_carry_the_resolved_enterprise_contextis now parametrized with one case per request-time call site (post/edit text+card, delete, reactions add/remove + reaction-event parent lookup, ephemeral text+card, fetch_messages both directions, fetch_message thread+DM, link-preview fetch, fetch_channel_messages both directions, list_threads, fetch_thread, fetch_channel_info,_lookup_channel,_lookup_user, open_dm, open/update modal, publish_home_view, start_typing, set_assistant_status/title, set_suggested_promptsapi_call). Each assertsteam_id == "T_GRID_1"andclient_context_team_idexactly when the call targets the originating channel. Addedtest_a_caller_specified_client_context_team_id_is_kept. Test-only, no adapter change.self._with_token_kwargs(withdict(at each of the 31 sites one at a time kills 30/31, and removing theclient_context_team_id is Noneguard is killed. The one survivor is the single-workspaceauth_testininitialize(as upstreamindex.ts:1698). It runs at init time outside any request context, so the wrap is a no-op there and a test would be artificial.gpt-6-astra: 1 round on
4200922. Verdict: "No actionable regressions found in the diff against the specified merge base."Bots: CodeRabbit skipped the draft and then hit its rate limit, so it left no review comments. There are no gemini comments.
CI: all checks green on
4200922(Tests 3.12/3.13, Lint & Type Check, CodeQL).Closes #268
Part of #184