Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(slack): paginate conversations.list in listSlackChannels

merged#2517CedarCopilot

CedarCopilot wants to merge 0 commits into staging from fix/sherlock-slack-list-channels-pagination-20260817-205526

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Aug 17, 2026, 8:59 PM
  2. Merged
    Aug 18, 2026, 4:59 PM
  3. Live on prod
    Aug 18, 2026, 4:59 PM
  4. Observed 2 days
    Aug 18, 2026, 4:59 PM
  5. Pipelines steady after this deploy
    Aug 18, 2026, 4:59 PM
  6. Unobserved

    Live on prod, no production signal yet

    Aug 21, 2026, 4:17 PM

Behaviors Libra is checking

Slack channel discovery now surfaces Slack conversations.list failures, including Slack error codes such as rate limits, instead of returning an incomplete channel list.Inconclusivelow confidence

OTEL fallback found no prod spans matching listSlackChannels / Slack conversations.list since deploy.

prod, checked Aug 21, 2026, 3:17 PM
Slack channel discovery now follows conversations.list cursors and includes channels from all available result pages, allowing delivery to later-sorting channels.Inconclusivelow confidence

OTEL fallback found no prod spans matching listSlackChannels / Slack conversations.list since deploy.

prod, checked Aug 21, 2026, 3:18 PM
Slack channel discovery stops after 50 pages to prevent a malformed or repeating Slack cursor from causing an infinite request loop.Inconclusivelow confidence

OTEL fallback found no prod spans matching listSlackChannels / Slack conversations.list since deploy.

prod, checked Aug 21, 2026, 4:17 PM

Libra has verdicts on 0 of 3 tracked behaviors on prod; 3 are still being checked. Libra checks hourly for 3 days after each deploy.

What was wrong

listSlackChannels (apps/server/src/services/integrations/slack/slack-api.ts) fired a single Superglue slack-list-channels call and returned only the first page. Slack's conversations.list is cursor-paginated (~200 channels/page), so any channel that sorts onto a later page was invisible to the agent.

Impact: an org report whose target Slack channel sorts late (recently created) fails to find it. Concretely , Pirros' weekly "barriers to buy" report posts to #barriers-to-buy, which is public and the cedar_mail bot is a member, but it sits at position ~140 of 166 channels (page 9 of 9). The report agent's list-slack-channels never saw it, marked itself "channel not found in workspace", and skipped both the Slack post and its KB-doc write.

Evidence

  • Direct paginated conversations.list (bot cedar_mail / U0BL034EYTS): #barriers-to-buy (C0BNWFQNRJ5) , public, is_member=true, found only after following next_cursor through 9 pages.
  • Report runs 98426a2a, 21cd2c61 failed with "Slack channel #barriers-to-buy not found in workspace" while the underlying data query succeeded (42 rows).

What changed

listSlackChannels now calls Slack conversations.list directly (same pattern as the file's other helpers) and follows next_cursor to the end, aggregating every page. Bounded by a 50-page safety cap (logs a warning if a cursor is still pending at the cap). Requests public_channel,private_channel with exclude_archived=false to preserve the previous return shape.

How to test

pnpm --filter @zero/server exec vitest run src/services/integrations/slack/__tests__/list-slack-channels-pagination.test.ts , 3 cases: follows the cursor and returns a channel found only on page 2; single page when no cursor; throws on ok:false.

After deploy: the barriers-to-buy report (and any channel-posting automation targeting a late-sorting channel) resolves the channel and posts.

Related

Paired with #2516 (org-cron representative connection health) , both were root causes of Pirros' weekly report not delivering. This one is why it failed even when re-run under a healthy rep.

🤖 Generated with Claude Code

Greptile Summary

This PR replaces the single-page Superglue channel-listing call with direct cursor-based Slack pagination, preserving channel normalization and adding focused pagination/error tests.

  • Retrieves public and private channels in pages of 200.
  • Aggregates pages until Slack returns no cursor, with a 50-page safety cap.
  • Adds tests for multi-page aggregation, single-page completion, and Slack API errors.

Confidence Score: 4/5

The PR appears safe to merge, with only a non-blocking import-convention issue in the new test.

The pagination implementation follows Slack cursors, aggregates each successful page, and preserves the fields consumed by current callers; the only accepted concern is the test's use of relative imports.

Files Needing Attention: apps/server/src/services/integrations/slack/tests/list-slack-channels-pagination.test.ts

Important Files Changed

FilenameOverview
apps/server/src/services/integrations/slack/slack-api.tsReplaces one-page workflow execution with bounded direct Slack cursor pagination while retaining the existing normalized return shape.
apps/server/src/services/integrations/slack/tests/list-slack-channels-pagination.test.tsAdds focused pagination and error tests, but its new relative imports violate the repository's absolute-import convention.

Sequence Diagram

rendering diagram…
Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/server/src/services/integrations/slack/__tests__/list-slack-channels-pagination.test.tsno production surface mapped
  • apps/server/src/services/integrations/slack/slack-api.tsno production surface mapped