You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
Activity