Problem
adapter-slack owns the channel path segment contract and has changed it twice:
<channelId>__<slug> — current, since 69761f13 ("v2 parity", 2026-05-12)
<slug>--<channelId> — legacy, adapter-slack <= 0.2.2
<channelId> — bare
The forward direction is owned here (channelSegmentV2 / channelSegmentLegacy in path-mapper.ts), and the inverse exists here too — extractSlackChannel in writeback.ts — but it is private. Consumers that need to recover a channel id from a path have to re-implement it.
Why it matters
Cloud did exactly that, and it broke. When form 1 landed, Cloud's watch matcher still assumed bare ids, so every channel-scoped Slack trigger silently stopped matching its own events — agents stopped waking on @mention, with no error anywhere. Only agents mis-scoped to /slack/channels/** kept working, waking on every message in the workspace. That went unnoticed for months.
Cloud now carries a second copy of this parser (cloud#3231). If the separator, normalization, or accepted id shape changes again, it silently desyncs the same way.
Proposal
Export the inverse, reusing the existing extractSlackChannel logic so there is one implementation:
/** Recover the canonical Slack channel id from a path segment, in any emitted form. */
export function slackChannelIdFromPathSegment(segment: string): string | null;
Handling all three forms, keyed off the id rather than the slug (channel names may contain underscores that slugification lossily replaces, so the id token is load-bearing).
Then writeback.ts consumes it internally and Cloud consumes it instead of its local regex — turning "the contract moved and a downstream matcher silently rotted" into a compile-time coupling.
Raised by an automated review on cloud#3231; filing here because the fix belongs with the contract owner.
Problem
adapter-slackowns the channel path segment contract and has changed it twice:<channelId>__<slug>— current, since 69761f13 ("v2 parity", 2026-05-12)<slug>--<channelId>— legacy, adapter-slack <= 0.2.2<channelId>— bareThe forward direction is owned here (
channelSegmentV2/channelSegmentLegacyinpath-mapper.ts), and the inverse exists here too —extractSlackChannelinwriteback.ts— but it is private. Consumers that need to recover a channel id from a path have to re-implement it.Why it matters
Cloud did exactly that, and it broke. When form 1 landed, Cloud's watch matcher still assumed bare ids, so every channel-scoped Slack trigger silently stopped matching its own events — agents stopped waking on
@mention, with no error anywhere. Only agents mis-scoped to/slack/channels/**kept working, waking on every message in the workspace. That went unnoticed for months.Cloud now carries a second copy of this parser (cloud#3231). If the separator, normalization, or accepted id shape changes again, it silently desyncs the same way.
Proposal
Export the inverse, reusing the existing
extractSlackChannellogic so there is one implementation:Handling all three forms, keyed off the id rather than the slug (channel names may contain underscores that slugification lossily replaces, so the id token is load-bearing).
Then
writeback.tsconsumes it internally and Cloud consumes it instead of its local regex — turning "the contract moved and a downstream matcher silently rotted" into a compile-time coupling.Raised by an automated review on cloud#3231; filing here because the fix belongs with the contract owner.