CedarCopilot wants to merge 2 commits into staging from fix/slack-channel-link-scope
Not deployed
Libra has no production signal for this change yet because it has not deployed. Libra checks hourly for 3 days after each deploy.
Channel linking selected candidates from listSlackChannels, which calls Slack's conversations.list , every channel in the workspace. Corgi names customer channels {company}-corgi, so an endsWith: "-corgi" pattern matched 445 workspace channels for a user who is a member of 16. Each candidate costs a Superglue members fetch plus DB work at SLACK_SYNC_CONCURRENCY_LIMIT = 1, exhausting both the Step Functions 10-minute state timeout and the HTTP gateway timeout , while linking nothing.
The codebase already documented the distinction, in userIsInSlackChannel's own docstring: users.conversations is what the caller is IN; conversations.list is everything in the workspace. The selection path never applied it.
A first hypothesis was disproved by the logs. I suspected a missing Effect.timeout around a hung linkSlackChannelOnlyDirect. Worker logs show every batch emitting initial_sync_step_complete and zero initial_sync_slack_channel_error rows. Nothing hung , there was simply far too much work.
listUserSlackChannels (new, slack-api.ts) , users.conversations, paginated to the end, user token. Linking uses it. listSlackChannels keeps whole-workspace semantics because channel pickers legitimately need the full list.slack-sync.ts) , linkSlackChannelOnly returned success:false whenever no channel_containers row existed, and containers were only ever created by message ingest. That made link-only sync unable to onboard a new user at all: every channel is uningested by definition. Now bootstraps via the idempotent ensureContainerForMember.link-matching-channels.ts) , SlackConfigSchema.autoSelectPatterns is z.array, but live connections stored a bare object. safeParse rejects the whole config, and the old path returned {linked:0, skipped:0, errors:0} behind a console.warn , indistinguishable from "nothing configured". Now a structured error counted as errors: 1.{"type":"initial_sync_step_complete","step":"slack_sync_batch_6","syncedCount":0,"channelCount":30}
[SLACK_LINK] User r0WQā¦: linking 445 changed/new channel(s) (0 linked, 14 archived, 0 unchanged)
initial_sync_slack_channel_error ā zero rows. Postgres: channel_container_users Liam 0/0 vs Rhocar 91/91.
Full report: docs/bugs/slack-channel-link-timeout-2026-08-26.md
9 new across 2 files (5 failing before the fix, pasted in the report). Full Slack suite: 94 passing / 12 files. pnpm --filter @zero/server run types clean for touched files (2 pre-existing errors in phase2c-seed-playbook.ts are unrelated and predate this branch).
6 Slack connections across 5 other orgs still store autoSelectPatterns as an object and are silently no-oping their periodic channel linking. Corgi's two are repaired. The rest need a decision before touching live customer config.
š¤ Generated with Claude Code
The PR scopes automatic Slack channel linking to channels joined by the connected user, bootstraps missing channel containers before evaluating links, and makes malformed sync configuration visible as an error.
users.conversations integration using the connected user's token.The PR appears safe to merge after considering a non-blocking privacy improvement to avoid logging stored Slack channel configuration.
The membership-scoped listing and container bootstrap align with existing Slack and container contracts; t
Libra has not measured any production surfaces for this change yet.