fix(slack): declare im:read — conversations.info needs it for a DM - #1660
Conversation
`conversations.info` takes one of `channels:read` / `groups:read` / `im:read` / `mpim:read` depending on the conversation type. The manifest declared three of the four and omitted the DM arm. Not cosmetic. `sendMessage` into a DM asks `getChannelInfo` whether the target is one, and the lookup is written to fall back rather than fail a send that already happened — so a refused call reads as "not a DM". That keys the outbound session as a channel thread while the inbound messages key it as a DM, and the conversation forks. `getCurrentChannel` in a DM also loses its name and isIm. Three signals agreed: Slack's own scope table, our test app which someone had already added `im:read` to by hand, and both comparable Slack apps carrying it. This is the scope-arrives-with-its-feature rule holding rather than bending — the consumer shipped long ago and only the grant was missed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Approved — no blocking findings.
im:read is the missing DM-specific permission for conversations.info; Slack’s official method reference lists it for direct-message access, while the existing im:history and im:write grants cover different operations. The daemon depends on this lookup when classifying an outbound Slack destination for its canonical thread key and when projecting current-channel metadata.
Adding the scope to the canonical SLACK_BOT_SCOPES list reaches the manual manifest, Control Plane app creation and managed-manifest refresh, platform OAuth authorization, and grant fencing. The Control Plane and web drift guards were updated consistently, and the change does not introduce a second scope source.
Verification: the checkout was a clean synthetic merge with parents exactly matching the trusted base and head; I inspected the complete diff and traced every scope consumer, and git diff --check passed. I could not rerun the focused Vitest suites because this isolated runner lacked repository-local test binaries and Corepack’s pinned pnpm download was blocked by restricted DNS/network policy; that environment-only gap does not change the verdict for this narrow manifest correction.
sent by review-bot (Codex · gpt-5.6-sol) · open in session
conversations.infotakes one ofchannels:read/groups:read/im:read/mpim:readdepending on the conversation type. The manifest declared three of the four and omitted the DM arm.Not cosmetic.
sendMessageinto a DM asksgetChannelInfowhether the target is one, and that lookup is deliberately written to fall back rather than fail a send that already happened — so a refused call reads as not a DM. The outbound session is then keyed as a channel thread while the inbound messages key it as a DM, and the conversation forks.getCurrentChannelin a DM also loses itsnameandisIm.Found by diffing our scope list against two comparable Slack apps. Three signals agreed: Slack's own scope table for the method, our test workspace app which someone had already added
im:readto by hand, and both of those apps carrying it.This is the scope-arrives-with-its-feature rule from #1655 holding rather than bending — the consumer shipped long ago and only the grant was missed.
Verification:
typecheck,lint,format:checkclean; CP manifest unit 18, web console platform 21, both drift guards updated.🤖 Generated with Claude Code