Skip to content

feat(api): list the channels a bot can post in - #372

Merged
Makisuo merged 4 commits into
mainfrom
feat/bot-api-list-channels
Oct 31, 2026
Merged

Makisuo merged 4 commits into
mainfrom
feat/bot-api-list-channels

fix(api): a deleted org answers a bot as not installed

d8531cc
Select commit
Loading
Failed to load commit list.
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.

  • listOrganizationChannels serves public channels plus the private ones the bot joined
  • requireBotScope refuses a scoped bot without channels:read
  • requireBotMember now also refuses a soft-deleted org, for both bot lookups

Before merge

  • Manual · Add channels:read to 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)
  • requireBotMember runs before the query and rejects a deleted org or a non-member bot user (bot-install-grants.ts:138-156)
  • Unqualified $.organizationId in 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.