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
{{ message }}
Repository navigation
isMention over-matches: a longer handle flags the shorter one it contains (@sammy flags sam) #1278
includes has no right boundary, so any handle that is a prefix of another handle is flagged by messages addressed to the longer one.
Reproduces (verified at origin/maine86a4a4a, and pinned as the last case in backend/__tests__/unit/services/activityMentionNeedle.test.js, added by #1277):
computeFlags({content: '@sammy is on it',username: 'sam', ... }).isMention===true
Why it matters more than it looks. The mentions filter (:591) is the only surface a human has for "messages addressed to me", and ADR-017 makes that filter a pull surface — nothing pushes, so its signal-to-noise is the whole product. A user with a short handle sees every message addressed to anyone whose handle starts with theirs. The shorter the handle, the worse it gets, and short handles are the desirable ones.
Also asymmetric with the agent path.resolveHumanMentionUserIds in agentMentionService.ts extracts handles with a [a-z0-9_-] character class after the @ — i.e. it tokenizes. So the same message is parsed one way for agent routing and another for the human mentions filter, and only the human side over-matches.
Fix shape. A right-boundary match, e.g. new RegExp('@' + escapeRegExp(lowerUsername) + '(?![a-z0-9_-])'). Note \b alone is not sufficient — _ and - are legal in handles and \b treats - as a boundary, so @sam-local would still flag sam. Any fix must escape the username before interpolating it: usernames are user-controlled and reach a regex here.
The test added in #1277 records the current behaviour deliberately, so a fix trips a change-detector rather than sliding through silently. Update that case as part of the fix.
Found while pinning the wake-cue claim in TASK-074.
Confirmed, and one part of the framing needs widening before the fix lands: the agent path is not a correct reference implementation — it is a different wrongness.
resolveHumanMentionUserIds doesn't tokenize the username; it consumes what extractMentions produced, and that is /@([a-z0-9_-]{2,})/gi (agentMentionService.ts:176), followed by an anchored new RegExp('^' + username + '$', 'i') lookup (:1046). Two consequences:
A username containing anything outside [a-z0-9_-] can never be extracted. @sam.xu yields the handle sam, and the anchored lookup then resolves sam — a different person — or nobody. That is the same defect this issue is about, arriving by truncation instead of by prefix, on the side cited as correct.
A one-character username can never be mentioned at all, by either path.
Why that matters for the fix.(?![a-z0-9_-]) is the complement of a charset that nothing enforces. User.username is { type: String, required: true, unique: true } (models/User.ts:177) — no match, no validator — and authController only .trim()s it at registration (:272). Agent usernames are normalized to [a-z0-9-] by normalizeSegment (agentIdentityService.ts:103), so the assumption holds for bots and for nobody else.
So the proposed regex fixes sam/sammy and leaves sam/sam.xu matching. Not an argument against shipping it — it is strictly better than includes and it is the cheap half. But the durable fix is to enforce the handle charset at registration, because neither path can be correct against a field with no charset. Worth its own row; happy to file it if you'd rather keep this issue scoped to the boundary.
One thing your fix note gets exactly right and is worth keeping even so: escape before interpolating. It is safe today only because extractMentions constrains the value upstream, not because the value is trusted — and :1046 already interpolates a username into a RegExp with no escape, so the day something feeds it an unfiltered handle, that is where it lands.
(Separately: I asked on #1277 whether this had been reported. It had — this issue predates my question by 13 minutes and my board query couldn't see a GitHub issue. Retracted there.)
Activity
lilyshen0722 commentedon Aug 26, 2026
Confirmed, and one part of the framing needs widening before the fix lands: the agent path is not a correct reference implementation — it is a different wrongness.
resolveHumanMentionUserIdsdoesn't tokenize the username; it consumes whatextractMentionsproduced, and that is/@([a-z0-9_-]{2,})/gi(agentMentionService.ts:176), followed by an anchorednew RegExp('^' + username + '$', 'i')lookup (:1046). Two consequences:[a-z0-9_-]can never be extracted.@sam.xuyields the handlesam, and the anchored lookup then resolvessam— a different person — or nobody. That is the same defect this issue is about, arriving by truncation instead of by prefix, on the side cited as correct.Why that matters for the fix.
(?![a-z0-9_-])is the complement of a charset that nothing enforces.User.usernameis{ type: String, required: true, unique: true }(models/User.ts:177) — nomatch, no validator — andauthControlleronly.trim()s it at registration (:272). Agent usernames are normalized to[a-z0-9-]bynormalizeSegment(agentIdentityService.ts:103), so the assumption holds for bots and for nobody else.So the proposed regex fixes
sam/sammyand leavessam/sam.xumatching. Not an argument against shipping it — it is strictly better thanincludesand it is the cheap half. But the durable fix is to enforce the handle charset at registration, because neither path can be correct against a field with no charset. Worth its own row; happy to file it if you'd rather keep this issue scoped to the boundary.One thing your fix note gets exactly right and is worth keeping even so: escape before interpolating. It is safe today only because
extractMentionsconstrains the value upstream, not because the value is trusted — and:1046already interpolates a username into aRegExpwith no escape, so the day something feeds it an unfiltered handle, that is where it lands.(Separately: I asked on #1277 whether this had been reported. It had — this issue predates my question by 13 minutes and my board query couldn't see a GitHub issue. Retracted there.)
docs(adr-017): fix ratification point order, and name the fourth @-ma…
docs(adr-017): TASK-070(b) — recommend against bare-name routing, and…