Skip to content

fix(slack): dispatch bot-id-only mentions from the message handler - #634

Open
yzxcj797 wants to merge 2 commits into
yc-software:mainfrom
yzxcj797:fix/bot-id-mention-dispatch-630b
Open

fix(slack): dispatch bot-id-only mentions from the message handler#634
yzxcj797 wants to merge 2 commits into
yc-software:mainfrom
yzxcj797:fix/bot-id-mention-dispatch-630b

Conversation

@yzxcj797

@yzxcj797 yzxcj797 commented Aug 20, 2026

Copy link
Copy Markdown

Fixes the dispatch half of #630 (step 2 of the issue's fix sketch). Stacked on #632 (the recognition commit — mentionsBot/stripMention both-id forms come from there); merge that first. Together they close #630 end to end.

What still dropped after recognition

Even with mentionsBot recognizing the bot-id form, the message handler's channel path assumed app_mention would carry every mention: it returned before dispatch for top-level messages (if (!threadReply) return) and routed thread replies into the ambient stake-followed path (unprompted: true) when the mention wasn't the user-id form. Slack fires app_mention only for the bot-user-id form — the bot-id form had no dispatcher at all.

The dispatch

The message handler now dispatches the addressed turn itself, scoped to exactly the class app_mention won't fire for:

const botIdMentionOnly =
  Boolean(ids.ownBotId) &&
  text.includes(`<@${ids.ownBotId}>`) &&
  !(ids.botUserId && text.includes(`<@${ids.botUserId}>`));
  • top-level bot-id-only mention → dispatched as an addressed turn (previously: mirrored handled=f, silently dropped);
  • thread-reply bot-id-only mention → dispatched addressed — unprompted is deliberately omitted: the sender typed @qm; the ambient flag belongs to the stake-followed path alone (previously: misdispatched as ambient carrying the raw <@B…> token);
  • both forms present → the user-id form routes through app_mention as before; the scoping makes the two paths disjoint, so no double dispatch (pinned by a test that emits both events for the same ts and asserts exactly one turn);
  • neither form → unchanged ambient/stake-followed behavior.

The mirrored row reads handled for every dispatched mention — the incident's handled=f signature for a message the bot actually answered is gone.

Tests

  • a bot-id-form top-level mention dispatches an addressed turn — turn dispatched, text stripped, no unprompted, mirror handled. Fails on the base (0 turns).
  • a bot-id-form thread reply dispatches ADDRESSED, not ambientunprompted undefined. Fails on the base (ambient dispatch).
  • a user-id mention still routes through app_mention, not the message handler — both forms in one message, both events emitted, exactly one turn (disjointness).

Full identity suites (slack-message-gating + slack-mrkdwn + slack-identity + slack-index.integration): 120/120 on this stack; the two new dispatch tests fail on the recognition-only base (43/2). tsc --noEmit clean.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

A hand-typed @qm can arrive encoded against the bot id (`<@botId>`)
rather than the bot user id (`<@ubot>`) — both are legitimate mentions
of the same single-bot app (bots.info maps one to the other), but Slack
fires no app_mention for the bot-id form, and message-gating only
recognized the user-id form. So a top-level B-form mention was silently
dropped (mirrored handled=f, no app_mention, no dispatch), and a B-form
thread reply skipped the addressed-turn early return and misdispatched
as an unprompted ambient turn carrying the raw <@b…> token (yc-software#630).

Recognition now covers both forms at every site the user-id form was
read:

- mentionsBot(text, botUserId, ownBotId) — both forms; the two-arg
  call shape is unchanged for existing callers;
- threadHasBotStake inherits the fix (it calls mentionsBot);
- stripMention strips both forms, including the legacy `<@id|label>`
  shape, so the core never receives the raw token for an addressed
  turn;
- the events.ts thread-reply gate and the mirror's mentionsSelf flag
  pass the bot id through (it is already in scope at both sites).

Name resolution deliberately does NOT feed the bot id to users.info
(no B→U translation exists there); the mirror's existing
auth.test-derived identity covers display naming, and callers that
need the bot's handle special-case their own id.

Message-handler dispatch of the B-form top-level mention (the exact
class app_mention won't fire for) is left as the follow-up the issue
sketches — the recognition fix here is the prerequisite and stops both
silent drops and ambient misdispatch in threads.

Fixes the recognition half of yc-software#630
A bot-id-form mention (<@b…>) never triggers Slack's app_mention event
— only the bot-user-id form does — so even with recognition fixed, the
message handler returned before dispatch for top-level messages and
routed thread replies into the ambient stake-followed path (yc-software#630 step
2).

The message handler now dispatches the ADDRESSED turn itself, scoped to
exactly the class app_mention won't fire for: the bot-id form is
present and the user-id form is absent. A message carrying both forms
still routes through app_mention alone (no double dispatch — the
existing dedupe key plus the scoping make the two paths disjoint), and
a bot-id-only thread reply dispatches addressed rather than unprompted
— the sender typed @qm; the ambient flag belongs to the
stake-followed path alone. The mirrored row reads handled for every
dispatched mention.

Stacked on the recognition commit (mentionsBot/stripMention both-id
forms); together they close yc-software#630 end to end.

Fixes the dispatch half of yc-software#630
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bot-id-form channel mentions (<@B…>) aren't recognized: top-level ones are silently dropped, in-thread ones misfire as ambient

1 participant