CopilotKit / CopilotKit/outpost
Slack adapter correctness — blocks the Slack go-live acceptance criteria
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7
- Forks
- 3
- Avg merge
- 7d 16h
- Merged PRs (30d)
- 15
Description
Why this blocks the Slack go-live
The Slack adapter has correctness bugs that #63's acceptance checklist cannot pass. Worth
fixing before starting #62, because the roadmap currently frames Slack as a wiring task —
the code is complete, but it is not correct.
Found during a code review of an unrelated PR; each item was verified against source.
Findings
1. postResponse discards the message ts — feedback tracking cannot work.
packages/outpost/shared/src/platforms/slack.ts hardcodes return undefined instead of
returning chat.postMessage's ts. The caller at
packages/outpost/queue/src/handlers/ai-response.ts:218 assigns that return to
externalCommentId, so Outpost never records which Slack message it posted. #63 explicitly
tests the AI response posting back and feedback being attributed — that cannot pass.
2. Missing event.ts yields threadId: '', so unrelated tickets collide.
parseInboundEvent produces threadId: '' when ts is absent, and sourceId becomes
"C123:". Every malformed event maps onto the same ticket lookup key.
3. Unvalidated cast; bot_id/subtype never checked — bot-loop risk.
parseInboundEvent casts rawEvent without inspecting the declared bot_id/subtype
fields, so the bot can respond to bot messages.
4. Slack's 3000-char Block Kit section cap is ignored.
The whole response goes into one section. Over the cap, chat.postMessage throws
invalid_blocks, and the caller only logs — so a long AI answer is silently dropped and the
user gets nothing. response.parts and response.buttons are also ignored, which is the
other half of why there is no feedback path.
5. mrkdwn is fed GitHub Markdown. Blocks declare type: 'mrkdwn' but receive
**bold** and [text](url), which Slack renders literally.
6. Empty-string profile fields defeat the fallbacks. fetchUserInfo uses ?? on
display_name/email/image_72; Slack returns "" for unset profile fields, and "" is not
nullish, so displayName becomes '' rather than falling back to real_name.
7. Blanket catch reports success. The catch around users.info turns a revoked token,
a missing scope, or a rate limit into a "success" that persists the raw Slack ID as the
username, with only a console.warn.
Acceptance criteria
-
postResponsereturns the posted messagets; a Slack ticket ends up with a non-null
externalCommentId, and 👍/👎 attribution works end to end - A missing
tsis rejected rather than producing an emptythreadId - Bot-authored messages (
bot_id/subtype) are ignored, with a test - Responses over 3000 chars are split across blocks and post successfully
- Markdown is converted to Slack
mrkdwn(or blocks are built fromresponse.parts) - Empty-string profile fields fall through to the next fallback
-
fetchUserInfodistinguishes auth/scope/rate-limit failures from a genuinely missing user
Related: #62, #63
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/outpost/shared/src/platforms/slack.ts, then trace the caller at packages/outpost/queue/src/handlers/ai-response.ts:218 and the #63 acceptance checklist. Verify each listed Slack adapter failure, including message IDs, event validation, block limits, formatting, profile fallbacks, and user-info errors. Done means all acceptance criteria pass, including tests for bot-authored messages and end-to-end feedback attribution.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100