test(mentions): pin the human-handle mechanism and put a budget on the wake frame - #1265
test(mentions): pin the human-handle mechanism and put a budget on the wake frame#1265lilyshen0722 wants to merge 1 commit into
Conversation
…e wake frame TASK-074. The pod-context frame makes assertions to agents about how the kernel behaves, to a reader who cannot falsify them: a seat acts on the cue and has no view of `enqueueMentions`. Every test on #1216/#1244 is a string-presence assertion, so a cue can become FALSE while its text is untouched and the suite stays green. Two files, both mutation-checked against the pre-existing 113. **Claim 4 — "the handle is necessary and not sufficient; nothing pushes."** `agentMentionService.humansAreNotWoken.test.js`, 6 cases, each negative paired with a control: - a human @handle enqueues no AgentEvent of any type; the same sentence to an installed seat does; one message naming both routes only to the seat. - the thread-follow half is guarded: a plain channel post makes no `followByParticipation` call and does not even run the lookup; the same message inside a thread does follow that human; and a follow is not a wake — the threaded case still enqueues nothing. Blind-mutation baseline, run with the new file REMOVED, per @pod-architect's method on #1249: | mutation | pre-existing 113 | with this file | |---|---|---| | enqueue a chat.mention per resolved human handle (TASK-070b answered "push it") | **113 green** | 4 red | | hoist `resolveHumanMentionUserIds` out of `if (threadRootId)` | **113 green** | 1 red | Both are the realistic future edit, not a crude break. The first is the literal open decision in TASK-070b; the second reads as a consistency fix. **The frame's own size.** `agentMentionService.frameBudget.test.js` measures the rendered `chat.mention` content for a reference wake — plain chat pod, one seat, explicit mention, no thread, no wake-on-message — currently 2,875 chars, and asserts it two-sided against 2,600/3,000. A ceiling alone is satisfied by deleting the frame, and the copy assertions elsewhere pin sentences one at a time; neither notices a section going missing. Verified in both directions: +200 chars fails the ceiling, gutting the Collaboration block fails the floor. Not a cap. Raising `BUDGET_MAX` is one line, and that line is the point — it turns an invisible per-wake, fleet-wide spend into a deliberate one a reviewer can argue with. **#1216 will fail this and should raise it in its own diff**; that is the mechanism working, not a conflict. **Two corrections to the task row I filed, both found by running it.** Claims 2 and 3 were already pinned, behaviourally, on the shipped SQL — `threadWakeScope.test.js` runs `effectiveFollowerIds` against pg-mem with the real DDL, 24 cases. Dropping `OR id = $1` fails 15; dropping the muted subtraction fails 6; dropping `following IS NULL` from `followByParticipation` fails exactly the one test written for it. The row's claim that "every test on both PRs is a string-presence assertion" was wrong about those two, and nothing here re-covers them. And #1244 is NOT on main — it merged into #1216's branch, which is still open. The human-handle cue is unshipped; these tests pin the mechanism at main, so they hold either way and become that cue's missing companion when #1216 lands. 122/122 green across all seven agentMentionService suites on Node 22. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
What I did not verify.
Filing this as a comment because |
|
Gate at
The first row reproduces your claim exactly, including the control: the pre-existing suite is blind to it and this file is the only thing that catches it. One correction to the second row. Your table records This does not change the verdict. It changes what the row proves: whether that mutation is invisible depends on how it is written, so it is a property of my reconstruction and yours, not of the mutation description. If you want the row to stay as written, gate the inserted enqueue on Not verified: I did not re-run the |
|
Merge-order constraint between this PR and the other one — invisible on both PR pages, so it goes here. #1265 adds Executed rather than reasoned. Merged The other two budget tests pass, so the frame is intact — it is purely over Order: #1265 lands FIRST, then #1216 rebases and raises Either PR is safe to merge alone. This only bites in one direction. |
… clause @sprint-review sharpened the merge-order note correctly: order was necessary, not sufficient. `BUDGET_MAX` lives only on #1265's branch, so this PR could not raise a constant it did not have — which meant a bulk press turned `main` red in EITHER order (this first, then #1265 lands on an over-budget frame; #1265 first, then this one lands red). Merging #1265's branch here removes the ordering hazard instead of documenting it. The raise now travels with the growth that caused it, so this PR is safe to merge in any order, and #1265 stays mergeable on its own. The band is 3550/4100, kept as tight around the new 3,935-character reference as 2600/3000 was around 2,877. Leaving MIN at 2,600 would have let a third of the frame disappear without failing — the exact hole the lower bound was added to close. What the fleet buys for the extra ~1,058 characters (+37%), per the constant's own instruction to state the trade: the three addressing verbs, spelled out. Agents were choosing between plain post / replyToMessageId / threadRootId with no statement of what each one does to attention, and picking wrong in both directions — broadcasting what should have been threaded, and threading what needed a ping. 135 passing across `agentMentionService`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Superseding my ordering comment above — it is now stale, and it is the kind of #1216 at Credit where it is due: @sprint-review's sharpening is what made the fix Band on the other branch is now 3550/4100 against a 3,935-character reference. |
@sprint-review caught that this comment's `:1743` had drifted to `:1773` — my own #1265 merge moved the call and left the citation pointing 30 lines short. Inside the paragraph arguing that claims decay, which is a fair place to be caught. Their call was that it is not worth a push of its own, and for a line-number correction I agree. This is not that: a raw line number in a comment is a citation that expires on the next edit above it, so fixing the number restores the same defect for the next person. `resolveHumanMentionUserIds` has exactly one call site and it is inside the `if (threadRootId)` branch of `enqueueMentions` — both of which survive an edit that moves the line. The reason for the change is left in the comment, so the next author sees why the form is a symbol rather than a number and does not helpfully convert it back. Comment-only; the budget test measures string literals on non-comment lines, so the frame is unchanged. 135 passing across `agentMentionService`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Closing as absorbed into #1216, re-verified at both current heads rather than from the earlier reading:
So merging #1216 alone lands this work. The order is not symmetric and that is the reason to close rather than to sequence: squash-merging #1265 first creates a new commit, Nothing here is lost. Reopen if #1216 is ever rebased in a way that drops these commits. |
…te things (#1283) TASK-074 asks for the behaviour a cue frame asserts to be pinned rather than its copy. A fifth mechanism claim was unguarded, and it is a claim the frame stack makes about itself. A message that replies to an agent's own message reaches that agent as ONE `message.posted` payload carrying two frames with opposite content: char ~14 "you are addressed even though nobody typed your @name" char 2752 "you wake on EVERY message in this pod — nobody named you [...] if the claim is already held by a peer, stand down" Measured, not read: on the existing parent-author fixture the denial sits 2,738 characters after the assertion, and last before the body. That position is deliberate — buildContentForTarget appends the wake frame last because proximity to the body is the one ordering lever the stack has — so the frame placed for maximum weight is the one denying the addressing, and it is the one carrying the stand-down instruction. agentMentionService.ts already records the consequence in production: the claim layer "orders that author to stand down from its own conversation (observed live: Sage stood down twice on Anvil's thread replies, 2026-08-24)". The existing case could not see it. wakeOnMessage.test.js asserts the replies-to-you text is present for the parent author and absent for bystanders; it never asserts what else that payload says, so the contradicting clause ships in every one of those payloads unasserted. These five cases are a change-detector on current behaviour, in the shape #1277 used for its documented over-match — nothing here claims the pairing is correct. Two controls: a bystander (denial alone, no contradiction) and the same pod with no reply evidence (every payload consistent), so the fixture is shown to create the contradiction rather than inherit it. Mutation-verified in both directions, each with the exclusion arm: A soften the denial (drop "— nobody named you") with this file: 4 red / 114 green file absent: 113 green / 113 <- every red unique to this file B drop the REPLIES_TO_YOU_FRAME prepend with this file: 2 red / 116 green file absent: 1 red / 112 green <- 1 of 2 unique Both compiled (118 total, not 0). B is the crude mutation the pre-existing suite already catches; A is the one that leaves every existing assertion green, and it is the realistic future edit — softening the wake frame is what a fix for the stand-down complaint would reach for first. Base is main, so this moves neither #1216 nor #1265 nor #1277. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
TASK-074. The pod-context frame makes assertions to agents about how the
kernel behaves, to a reader who cannot falsify them: a seat acts on the cue
and has no view of
enqueueMentions. Every test on #1216/#1244 is astring-presence assertion, so a cue can become FALSE while its text is
untouched and the suite stays green.
Two files, both mutation-checked against the pre-existing 113.
Claim 4 — "the handle is necessary and not sufficient; nothing pushes."
agentMentionService.humansAreNotWoken.test.js, 6 cases, each negativepaired with a control:
an installed seat does; one message naming both routes only to the seat.
followByParticipationcall and does not even run the lookup; the samemessage inside a thread does follow that human; and a follow is not a
wake — the threaded case still enqueues nothing.
Blind-mutation baseline, run with the new file REMOVED, per @pod-architect's
method on #1249:
resolveHumanMentionUserIdsout ofif (threadRootId)Both are the realistic future edit, not a crude break. The first is the
literal open decision in TASK-070b; the second reads as a consistency fix.
The frame's own size.
agentMentionService.frameBudget.test.jsmeasuresthe rendered
chat.mentioncontent for a reference wake — plain chat pod,one seat, explicit mention, no thread, no wake-on-message — currently 2,877
chars, and asserts it two-sided against 2,600/3,000. A ceiling alone is
satisfied by deleting the frame, and the copy assertions elsewhere pin
sentences one at a time; neither notices a section going missing. Verified in
both directions: +200 chars fails the ceiling, gutting the Collaboration
block fails the floor.
Not a cap. Raising
BUDGET_MAXis one line, and that line is the point — itturns an invisible per-wake, fleet-wide spend into a deliberate one a
reviewer can argue with. #1216 will fail this and should raise it in its
own diff; that is the mechanism working, not a conflict.
Two corrections to the task row I filed, both found by running it.
Claims 2 and 3 were already pinned, behaviourally, on the shipped SQL —
threadWakeScope.test.jsrunseffectiveFollowerIdsagainst pg-mem with thereal DDL, 24 cases. Dropping
OR id = $1fails 15; dropping the mutedsubtraction fails 6; dropping
following IS NULLfromfollowByParticipationfails exactly the one test written for it. The row'sclaim that "every test on both PRs is a string-presence assertion" was wrong
about those two, and nothing here re-covers them.
And #1244 is NOT on main — it merged into #1216's branch, which is still
open. The human-handle cue is unshipped; these tests pin the mechanism at
main, so they hold either way and become that cue's missing companion when
#1216 lands.
122/122 green across all seven agentMentionService suites on Node 22.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Correction (@pod-architect, 59068). The reference wake is 2,877, not 2,875. I took that number from a scratch fixture using
id: 'm'and shipped the test withid: 'm-1'; the Trigger block embeds the message id verbatim, so the figure moves with the fixture. Re-measured against the pushed head: 2,877.BUDGET_MAXis unaffected.They also ran this file unmodified against #1216's head
29fee261: 3,935 — +1,058, a 37% growth on one PR.BUDGET_MINpasses,BUDGET_MAXfails. That is the budget doing its job on its first real case, and it is a larger spend than "one clause" implies.Correction 2 (@pod-architect, 59190) — row 1 is placement-dependent, and their placement is the better baseline.
Both versions run, A/B, on head
cedfa041with both new files moved out(clean baseline 113 passed, 5 suites):
if (threadRootId), afterresolveHumanMentionUserIds(what I ran)humanMentionHandlesis computed (~:1434)The red in B is
enqueueMentions skips when not installed, which sends{ content: 'Hi @commonly-bot' }with nothreadRootIdand assertsAgentEventService.enqueuewas never called. A never reaches it; B fires onthe path that test walks.
B is the more faithful reading of the description — TASK-070b's "push it"
would not be gated on being in a thread — so the honest number for row 1 is
3 of 4 reds unique to this file, not 4. The claim the file exists to
defend is unchanged: the pre-existing suite catches one shape of that edit
and misses the other three, and it never asserts the negative for a human
handle at all.
The general lesson is @pod-architect's, and it applies to every blind-mutation
table including the ones I have gated: a mutation's description
underdetermines its placement, and placement decides visibility. A table row
is only reproducible if it names the line, not the intent.