Repository navigation
feat(api): list the channels a bot can post in - #372
Merged
Merged
Maple Review Bot / Maple / review
succeeded
Oct 31, 2026 in 2m 25s
Confidence 9/10 · No issues found
🟢 Confidence 9/10 · safe to merge. Bot-scope and org-membership checks gate a new org-scoped read; both the HTTP test and the object test cover the refusals.
no findings · tests covered · risk high −1 · 2/2 new units observable
Adds GET /api/v1/organizations/:organizationId/bot-channels, one join of the bot user's channel_access against channels, gated by the bot's channels:read scope and its membership in a live org. The scoping and every refusal path are tested; safe to merge.
listOrganizationChannelsserves public channels plus the private ones the bot joinedrequireBotScoperefuses a scoped bot withoutchannels:readrequireBotMembernow also refuses a soft-deleted org, for both bot lookups
Before merge
- Manual · Add
channels:readto the Maple bots in the US and EU orgs, or their channel list answers 401
Production impact
Telemetry this change adds and removes (4)
- ➕ span name
ApiV1.listOrganizationChannels·apps/backend/src/routes/api-v1/bots.http.ts:172 - ➕ span name
OrgObject.requireBotMember·apps/backend/src/worker/objects/org/bot-install-grants.ts:131 - ➕ span name
OrgObject.botChannels·apps/backend/src/worker/objects/org/bot-install-grants.ts:191 - ➕ attribute
org.bot_channels·apps/backend/src/worker/objects/org/bot-install-grants.ts:208
What was checked
- Channel query filters
channel_access.organizationId; the table is keyed[userId, channelId], so no cross-org rows or duplicates (tables/channel-access.ts:13) requireBotMemberruns before the query and rejects a deleted org or a non-member bot user (bot-install-grants.ts:138-156)- Unqualified
$.organizationIdin an aliased join follows existing usage (integration-commands.http.ts:28-33)
Observability coverage: 2 of 2 changes observable
| Change | Kind | Observable | Evidence |
|---|---|---|---|
| GET /api/v1/organizations/:organizationId/bot-channels | inbound HTTP route | yes | Effect.withSpan("ApiV1.listOrganizationChannels") at api-v1/bots.http.ts:172, in the pattern of the neighbouring bot endpoints |
| OrgObject.botChannels RPC | durable object handler | yes | Effect.fn("OrgObject.botChannels") span with org.bot_channels count at bot-install-grants.ts:191-211 |
d8531cc · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.
Loading