test(mentions): pin the mechanism behind "addressing is never scoped by the thread" - #1249
Conversation
…by the thread" @sprint-review's gate on #1216 named the gap: the tests there pin the CUE TEXT, and the sentence the cue teaches would become a lie the moment someone added one narrowToThread call to the mention fan-out — with all 129 copy assertions green. Copy is not mechanism. This asserts the call graph instead. The discriminator is a seat that is mentioned but NOT wake-opted-in: it reaches the addressing fan-out and never the wake one, so any narrowToThread call observed under it came from the mention path. Every negative is paired with a control that DOES call it, since "not called" is indistinguishable from a mock nothing can reach. Demonstrated rather than asserted. A mutation that scopes addressing only for non-opt-in seats — leaving every existing fixture's call count intact — passes all 107 pre-existing tests in these suites and reds exactly two here. The cruder always-narrow mutation is caught incidentally by threadScoping's call-count assertions too; this file is what catches the one that isn't. Also corrects a comment the first draft of this test believed. agentMention Service:1123 said the scoping branch "runs only when !isRouted", contradicting the call-site comment at :1748 in the same file — there are two call sites and the second runs unconditionally, so a routed message's ambient companion IS scoped. Addressing survives because the chat.mention is already enqueued and the seat arrives inside excludeKeys, not because the branch is unreachable. Right conclusion, false mechanism, which is the kind of comment that makes the next reader's test wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Reviewed at Reproduced: 137/137 green across the five I could not reproduce the second row of your table, and I tried two different mutations to get there. Both landed in your row-1 cell instead — already caught by the pre-existing suites:
So on three independent axes now, the crude version is already pinned, and the file's marginal behavioural value rests entirely on the shaped mutation in your row 2 — the one cell I could not construct myself. Worth saying plainly rather than letting my confirmation read as broader than it is. Where I did find value nothing else covers — the structural test. I added a second Exactly one red, and every behavioural test green — including all 131 pre-existing. That is the case your comment claims for it ("a second call site added on a path no fixture exercises yet"), and it is unreachable by any behavioural test by construction. On vacuity: Two non-blocking notes.
What I did not verify: anything outside these six suites — I did not run the full backend suite, so I am not claiming the mutations were locally scoped. And I did not reproduce your row 2, as above. Separately, this PR corrects something I published. Reviewing #1216 I wrote that addressing is never thread-scoped because |
Closes @sprint-review's non-blocking note on #1216: "the tests pin the copy but nothing pins the mechanism — one call added to the mention path would make 'addressing is never scoped by the thread' a lie with all 129 test lines green."
Independent of the #1216 → #1244 stack. Touches neither branch's files beyond one comment, so it can land in any order.
What it pins
narrowToThreadruns on the wake fan-out and never on the addressing one. The discriminator is a seat that is mentioned but not wake-opted-in: it reaches the addressing fan-out and never the wake one, so anynarrowToThreadcall observed under it came from the mention path.Every negative assertion is paired with a control that DOES call it.
not.toHaveBeenCalled()passes just as well from a mis-pathedjest.mock, a renamed export, or a module the service no longer requires — three failures that render identically to "addressing is correctly unscoped".Demonstrated, not asserted
threadScopingcall-count assertions)The second row is the one that matters. The crude mutation is already caught by accident; this file is what catches the one that isn't. Stated explicitly because "my new test reds under a mutation" is worth nothing until you know the repo didn't already red without it.
A comment corrected on the way
agentMentionService:1123said the scoping branch "runs only when!isRouted". It is contradicted by the call-site comment at:1748in the same file: there are two call sites and the second runs unconditionally, so a routed message's ambient companion IS thread-scoped.Addressing survives for a different reason than the comment gave — the
chat.mentionis already enqueued by the time scoping runs, and the mentioned seat arrives insideexcludeKeys. Right conclusion, false mechanism. This file's first draft asserted the comment's version and failed, which is how it was found.Verification
agentMentionServicesuites plusthreadWakeScope, on Node 22.eslintinvocation, against 28 from the already-merged siblingagentMentionService.threadingIsNotAddressing.test.jsunder the identical invocation, same rule classes (import/no-unresolved,import/extensions,object-curly-newline,global-require,function-paren-newline). Baseline of the invocation, not a regression.🤖 Generated with Claude Code