From 0e48535189153837a036d3b51ba1a099e0c1de30 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Wed, 26 Aug 2026 08:15:06 -0700 Subject: [PATCH] test(activity): pin the @handle requirement the wake cue teaches agents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pod-context frame tells every agent, on every wake, that "human attention is matched on the literal @handle, so 'Sam should decide this' is addressed to nobody." That is a claim about ActivityService.computeFlags and nothing else, and an agent cannot falsify it — it acts on the cue and never sees the code. Nothing guarded it. Measured at origin/main e86a4a4a: dropping the '@' from `mentionNeedle` (so a bare name counts as a mention) leaves ALL 2033 backend unit tests green. Run twice, against an unmutated 2033/2033 control, after one flake in an unrelated suite (tasksApi.status-vocabulary) had to be ruled out. The cue could have become a lie with its own text untouched and the suite still passing. Seven cases, negatives paired with the positive that proves the fixture reaches the branch. Discrimination verified both ways: - drop the '@' -> 2 red (both negatives), 5 green incl. controls - swap for a \b regex -> 1 red, and only the documented over-match The last case pins a known defect rather than endorsing it: `includes` has no right boundary, so a message naming @sammy flags a user called sam. Recorded here so the next reader sees it is known; reported separately. Base is main, not the #1216/#1244 stack, so it lands in any order. Co-Authored-By: Claude Opus 5 --- .../services/activityMentionNeedle.test.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 backend/__tests__/unit/services/activityMentionNeedle.test.js diff --git a/backend/__tests__/unit/services/activityMentionNeedle.test.js b/backend/__tests__/unit/services/activityMentionNeedle.test.js new file mode 100644 index 000000000..e9cd6dd16 --- /dev/null +++ b/backend/__tests__/unit/services/activityMentionNeedle.test.js @@ -0,0 +1,61 @@ +/** + * The pod-context cue (#1216/#1244) tells agents, in the frame they receive on + * every wake: "human attention is matched on the literal @handle, so 'Sam + * should decide this' is addressed to nobody." + * + * That is a claim about THIS function and nothing else. An agent cannot + * falsify it — it acts on the cue and never sees the code. Before this file, + * nothing did either: dropping the '@' from `mentionNeedle` left all 2033 + * backend unit tests green (measured at origin/main e86a4a4a, twice, with an + * unmutated 2033/2033 control). The cue would have become a lie with its own + * text untouched and the suite still passing. + * + * So this pins the BEHAVIOUR, not the copy. The negative is the load-bearing + * case; each one is paired with the positive that proves the fixture reaches + * the branch at all. + */ +const ActivityService = require('../../../services/activityService'); + +const flagsFor = (content, username) => ActivityService.computeFlags({ + actor: { id: 'u1', type: 'user' }, + type: 'message', + action: 'posted', + content, + username, +}); + +describe('human mentions are matched on the literal @handle', () => { + it('a bare name is NOT a mention — the cue\'s central claim', () => { + expect(flagsFor('Sam should decide this', 'sam').isMention).toBe(false); + }); + + it('CONTROL: the same sentence with the handle IS a mention', () => { + expect(flagsFor('@sam should decide this', 'sam').isMention).toBe(true); + }); + + it('the @ must be adjacent — "@ sam" does not address sam', () => { + expect(flagsFor('@ sam should decide this', 'sam').isMention).toBe(false); + }); + + it('matching is case-insensitive on both sides', () => { + expect(flagsFor('Ping @SAM about this', 'Sam').isMention).toBe(true); + }); + + it('an empty username never matches, however the content reads', () => { + expect(flagsFor('@ everyone @', '').isMention).toBe(false); + }); + + it('the handle is found mid-sentence, not just at the start', () => { + expect(flagsFor('I think @sam owns this one', 'sam').isMention).toBe(true); + }); + + /** + * Documented over-match, pinned so the next reader sees it is known rather + * than believing the handle is matched as a whole token. `includes` has no + * right boundary, so a longer handle contains a shorter one. Reported + * separately; this test records the behaviour, it does not endorse it. + */ + it('KNOWN OVER-MATCH: a longer handle flags the shorter one it contains', () => { + expect(flagsFor('@sammy is on it', 'sam').isMention).toBe(true); + }); +});