A display name written in another case is kept alongside the mention, so the reader sees the name twice #469

Closed
opened 2026-08-13 14:09:53 +00:00 by coilyco-ops · 0 comments
Member

Filed by Angie (ENG), found while fixing the URL collision in #465. Same function, independent cause, so it gets its own record.

What happens

Roster of one member named Kai, against a reply that spells the name in lower case:

in : kai runs the server.
out: kai<@111> runs the server.

Discord renders <@111> as the display name, so the member reads kai@Kai runs the server.

Cause

The matcher folds case and the replacement does not:

pattern, err := regexp.Compile(`(?i)(^|[^\w<@])` + regexp.QuoteMeta(name) + `\b`)
...
return strings.TrimSuffix(match, name) + "<@" + userID + ">"

(?i) matches kai for a roster spelling of Kai. TrimSuffix then finds no Kai to trim, returns the match whole, and the mention is appended to a name that was supposed to be replaced by it.

The roster is built from the turn's transcript, so the spelling is whatever a member's display name is, and the reply is whatever the model wrote. Nothing holds those two to the same case. A model that opens a sentence with a lowercase handle, or writes Kai as KAI, is enough.

Confirmed on merged main

At 3108976, resolveMentions("kai runs the server.") with Kai -> 111 returns "kai<@111> runs the server." and reports the account resolved.

Fix

Cut on the match rather than on the roster's spelling. The submatch index already marks where the guard character ends and where the name ends, so the splice needs no assumption about length or case.

Acceptance

  • A name spelled in any case is replaced by the mention, not joined to it.
  • The four existing bounds are unchanged: under three runes, inside a longer word, once per person, and no text parsing by Discord.
  • The link bound from 465 is unchanged.

Being fixed in the same change as 465, because the splice that skips link spans is the same line that decides where the cut falls.

**Filed by Angie (ENG)**, found while fixing the URL collision in https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/465. Same function, independent cause, so it gets its own record. ## What happens Roster of one member named `Kai`, against a reply that spells the name in lower case: ``` in : kai runs the server. out: kai<@111> runs the server. ``` Discord renders `<@111>` as the display name, so the member reads `kai@Kai runs the server.` ## Cause The matcher folds case and the replacement does not: ```go pattern, err := regexp.Compile(`(?i)(^|[^\w<@])` + regexp.QuoteMeta(name) + `\b`) ... return strings.TrimSuffix(match, name) + "<@" + userID + ">" ``` `(?i)` matches `kai` for a roster spelling of `Kai`. `TrimSuffix` then finds no `Kai` to trim, returns the match whole, and the mention is appended to a name that was supposed to be replaced by it. The roster is built from the turn's transcript, so the spelling is whatever a member's display name is, and the reply is whatever the model wrote. Nothing holds those two to the same case. A model that opens a sentence with a lowercase handle, or writes `Kai` as `KAI`, is enough. ## Confirmed on merged `main` At `3108976`, `resolveMentions("kai runs the server.")` with `Kai -> 111` returns `"kai<@111> runs the server."` and reports the account resolved. ## Fix Cut on the match rather than on the roster's spelling. The submatch index already marks where the guard character ends and where the name ends, so the splice needs no assumption about length or case. ## Acceptance - A name spelled in any case is replaced by the mention, not joined to it. - The four existing bounds are unchanged: under three runes, inside a longer word, once per person, and no text parsing by Discord. - The link bound from 465 is unchanged. Being fixed in the same change as 465, because the splice that skips link spans is the same line that decides where the cut falls.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo#469
No description provided.