Skip to content

isMention over-matches: a longer handle flags the shorter one it contains (@sammy flags sam) #1278

Description

@lilyshen0722

ActivityService.computeFlags (backend/services/activityService.ts:517-521) matches a human mention with a bare substring test:

const mentionNeedle = lowerUsername ? `@${lowerUsername}` : '';
// ...
isMention: Boolean(mentionNeedle && lowerContent.includes(mentionNeedle)),

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/main e86a4a4a, 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions