Skip to content

Slack ticket mirror: reply-level dedup, surrogate-safe truncation, WebClient reuse #152

Open

Description

@NathanTarbert

Follow-ups deliberately left open by #150 (the Slack ticket mirror). All three were raised by the CR rounds on that PR and verified; none blocks the mirror shipping inert.

1. A duplicate reply job re-posts its message

handleSlackMirror makes the TicketExternalLink row the idempotent identity of the thread, so two jobs can no longer open rival threads. It does not dedupe reply posts: if a reply job is retried after its postMessage succeeded (a job timeout that does not cancel the handler, a throw in reportProgress, a re-thrown transient error), the same message is posted into the thread again.

Fixing this needs a per-message marker — e.g. a mirroredAt column on Message, or a metadata entry on the link row keyed by message id — checked before posting. That is a schema decision, which is why it was not smuggled into #150.

packages/outpost/queue/src/handlers/slack-mirror.ts — the idempotency docblock states this limitation explicitly.

2. truncate splits surrogate pairs

value.slice(0, max - 1) can cut an astral character (emoji, many CJK extension chars) in half, producing a lone surrogate in the Slack payload. Use Array.from / Intl.Segmenter or a code-point-aware slice.

3. A new WebClient per job

buildPoster constructs a fresh @slack/web-api client on every job. At mirror volumes this is minor, but it discards connection reuse and the client's rate-limit state, which is precisely the state you want shared across posts. Cache one client per token.

Found by: CR rounds 1-2 on #150.

Activity

  1. added
    roadmapTracked on the Outpost roadmap
    roadmap: nextRoadmap horizon: after launch path clears
    area: integrationsSlack, Teams, Linear, GitHub App, Postmark, Pathfinder, HubSpot
    on Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: integrationsSlack, Teams, Linear, GitHub App, Postmark, Pathfinder, HubSpotroadmapTracked on the Outpost roadmaproadmap: nextRoadmap horizon: after launch path clears

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions