fix(mentions): resolve names in prose only, so a link survives #472

Merged
coilyco-ops merged 1 commit from eng/mentions-skip-link-spans into main 2026-08-13 14:12:39 +00:00
Member

Resolution now runs on prose only. mentionSpans splits the reply on the same urlSpan the four prose validators mask with, and a link span is carried through untouched, so where a link starts keeps one definition rather than gaining a second.

The four corpus shapes from the issue now come back byte-identical, and nobody is reached for a link that merely contains their name.

The second defect, filed separately

The replacement joined the mention to the name instead of replacing it whenever the two differed in case:

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

TrimSuffix(match, name) cannot trim what (?i) was free to match. The splice cuts on the submatch index instead, which needs no assumption about the name's length or case, so this goes away in the same line that skips link spans. Confirmed on merged main at 3108976 before changing anything.

What did not change

The four bounds: under three runes, inside a longer word, once per person, and Parse still empty so Discord never reads the text. Each keeps its test.

One behaviour did change beyond the fix, and deliberately: a name that appears only inside a link no longer counts as resolved. It previously entered the allowed-mentions list, which is the ping harm the issue names second.

Tests

TestANameInsideALinkRewritesTheLink was a characterization test that asked to be removed once the behaviour was fixed. It is replaced by TestANameInsideALinkLeavesTheLinkAlone over the same corpus, plus both orderings of the mixed case where one reply carries the name in prose and in a link.

Quail: the corpus is in the tree and both halves run under ward exec gate. Extend it against this branch whenever you like.

closes #465
closes #469

Resolution now runs on prose only. `mentionSpans` splits the reply on the same `urlSpan` the four prose validators mask with, and a link span is carried through untouched, so where a link starts keeps one definition rather than gaining a second. The four corpus shapes from the issue now come back byte-identical, and nobody is reached for a link that merely contains their name. ## The second defect, filed separately The replacement joined the mention to the name instead of replacing it whenever the two differed in case: ``` in : kai runs the server. roster: Kai -> 111 out: kai<@111> runs the server. ``` `TrimSuffix(match, name)` cannot trim what `(?i)` was free to match. The splice cuts on the submatch index instead, which needs no assumption about the name's length or case, so this goes away in the same line that skips link spans. Confirmed on merged `main` at `3108976` before changing anything. ## What did not change The four bounds: under three runes, inside a longer word, once per person, and `Parse` still empty so Discord never reads the text. Each keeps its test. One behaviour did change beyond the fix, and deliberately: a name that appears **only** inside a link no longer counts as resolved. It previously entered the allowed-mentions list, which is the ping harm the issue names second. ## Tests `TestANameInsideALinkRewritesTheLink` was a characterization test that asked to be removed once the behaviour was fixed. It is replaced by `TestANameInsideALinkLeavesTheLinkAlone` over the same corpus, plus both orderings of the mixed case where one reply carries the name in prose and in a link. Quail: the corpus is in the tree and both halves run under `ward exec gate`. Extend it against this branch whenever you like. closes #465 closes #469
fix(mentions): resolve names in prose only, so a link survives
All checks were successful
ci / image-build (pull_request) Successful in 22s
ci / test (pull_request) Successful in 34s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
c0613441cb
A display name is matched anywhere in the reply and a URL is part of the
reply, so a member called eco, wiki, issues or main corrupted every link
containing their name and was pinged for it. Resolution now runs on the
same link spans the prose validators mask, so where a link starts has one
definition rather than two.

The replacement also cuts on the match rather than on the roster's
spelling of the name, which a case-insensitive pattern is under no
obligation to produce. A name written in another case was kept alongside
the mention it was supposed to become.

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
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!472
No description provided.