Repository navigation
fix(notifications): enforce the inbox's size bounds in NotificationCenter.post (#1666) - #1670
Conversation
|
@claude review |
|
Claude finished @fluck-boss's task in 3m 30s —— View job Review: enforce the inbox's size bounds in
|
…nter.post (risa-labs-inc#1666) risa-labs-inc#1644 bounded notification_post at the MCP provider, and risa-labs-inc#1660 put the snippet caps in the store. Two adjacent fixes on opposite conventions, and this one left a hole: post is documented as serving the host too, and a HOST-origin post met none of the three bounds. - MAX_TITLE_CHARS and MAX_MESSAGE_CHARS move to NotificationCenter and are required in post beside the existing blank-title check, for every origin. - A HOST source label longer than MAX_SOURCE_LABEL_CHARS is refused. An agent's label keeps risa-labs-inc#1619's truncation: an agent is not ours to fix, while a host label that long is a bug to catch where it is written. - The MCP tool keeps its check first so its refusal names the field and the limit. - Tests: over-long title and message refused from both origins with nothing stored; all three exactly at their limits accepted; host label refused while an agent's is cut. The provider test pins MAX_LIST_LIMIT < MAX_ENTRIES, which the clamp depends on. Mutation-checked: removing each of the three requires fails a named test. 416 tests across ai.rever.boss.notifications.* and ai.rever.boss.mcp.* pass; ktlint and detekt clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…latten host labels Review follow-up on risa-labs-inc#1670. - A host message over MAX_MESSAGE_CHARS is cut, ending in an ellipsis, never splitting a surrogate pair. A host builds its message from dynamic text (an exception message, a command's output), and refusing would turn a notice about one problem into a second problem on an error path. An agent's message is still refused, because the MCP tool checks first and names the limit. Titles are still refused from either origin. - A host label is flattened to one line like an agent's, and the cap is measured on the stamped label, which is exactly what is stored. So trimmed whitespace cannot tip a label over. - post's KDoc states the rule per field and origin, with @throws for the three refusals. MAX_ENTRIES states the aggregate bound, about 860,000 characters and more once escaped, as SnippetLibraryManager's does. - Tests: the at-limit test asserts every field is stored intact. New cases cover a host label padded with whitespace at the cap, a host label with a newline, a host message cut with its ellipsis, and a cut that lands on a surrogate pair. Mutation-checked: storing the host message uncut, dropping the surrogate guard, not flattening the host label, measuring it untrimmed, and not refusing the agent message each fail a named test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38308ac to
7a369be
Compare
|
Thanks. All four asks are in, in 7a369be, rebased onto current 1. A host message is cut, not refused. Your framing is right: a notice about a problem shouldn't fail into a second one. A HOST message over 4,000 characters is cut to the cap and ends in an ellipsis. The cut never splits a surrogate pair, since half an emoji would be stored as a lone surrogate. An AGENT message is still refused, because the MCP tool checks first and names the limit. Titles are still refused from both origins, since they are literals. 2. 3. Host labels are flattened like an agent's, rather than recording the gap. A newline in a host label now renders as one line. The cap is measured on the stamped label, which is exactly what is stored, so that one value is both checked and stored. 4. Tests:
Mutation check: each of these fails a named test:
All 26 of |
|
Merging. Gate complete: CI green on the head, Claude review has nothing blocking, my own read clean. |
…nter.post (#1666) (#1670) * fix(notifications): enforce the inbox's size bounds in NotificationCenter.post (#1666) #1644 bounded notification_post at the MCP provider, and #1660 put the snippet caps in the store. Two adjacent fixes on opposite conventions, and this one left a hole: post is documented as serving the host too, and a HOST-origin post met none of the three bounds. - MAX_TITLE_CHARS and MAX_MESSAGE_CHARS move to NotificationCenter and are required in post beside the existing blank-title check, for every origin. - A HOST source label longer than MAX_SOURCE_LABEL_CHARS is refused. An agent's label keeps #1619's truncation: an agent is not ours to fix, while a host label that long is a bug to catch where it is written. - The MCP tool keeps its check first so its refusal names the field and the limit. - Tests: over-long title and message refused from both origins with nothing stored; all three exactly at their limits accepted; host label refused while an agent's is cut. The provider test pins MAX_LIST_LIMIT < MAX_ENTRIES, which the clamp depends on. Mutation-checked: removing each of the three requires fails a named test. 416 tests across ai.rever.boss.notifications.* and ai.rever.boss.mcp.* pass; ktlint and detekt clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(notifications): cut a long host message instead of refusing it; flatten host labels Review follow-up on #1670. - A host message over MAX_MESSAGE_CHARS is cut, ending in an ellipsis, never splitting a surrogate pair. A host builds its message from dynamic text (an exception message, a command's output), and refusing would turn a notice about one problem into a second problem on an error path. An agent's message is still refused, because the MCP tool checks first and names the limit. Titles are still refused from either origin. - A host label is flattened to one line like an agent's, and the cap is measured on the stamped label, which is exactly what is stored. So trimmed whitespace cannot tip a label over. - post's KDoc states the rule per field and origin, with @throws for the three refusals. MAX_ENTRIES states the aggregate bound, about 860,000 characters and more once escaped, as SnippetLibraryManager's does. - Tests: the at-limit test asserts every field is stored intact. New cases cover a host label padded with whitespace at the cap, a host label with a newline, a host message cut with its ellipsis, and a cut that lands on a surrogate pair. Mutation-checked: storing the host message uncut, dropping the surrogate guard, not flattening the host label, measuring it untrimmed, and not refusing the agent message each fail a named test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Closes #1666.
#1644 bounded
notification_postat the MCP provider, and #1660 put the analogous snippet caps in the store. So two adjacent fixes landed on opposite conventions. This one also left a real hole:NotificationCenter.postis documented as serving the host too, and a HOST-origin post met none of the three bounds. The first host publisher would have reopened what #1644 closed, through a call that reviews as innocuous.The change
MAX_TITLE_CHARS(200) andMAX_MESSAGE_CHARS(4,000) move toNotificationCenter, so nothing stored exceeds them whoever posts. The inbox is rewritten in full on every post and listed without approval. Whether an over-long field is refused or cut depends on who wrote it and how:The label cap is measured on the stamped label, which is exactly what is stored.
post's KDoc states this per field, with@throwsfor the three refusals, andMAX_ENTRIESstates the aggregate bound: about 860,000 characters, more once JSON-escaped.NotificationMcpToolProvider.tooLongstays as the layer that runs first, so an agent's refusal still names the field and its limit instead of surfacing the store's exception. The provider imports the store's constants, so the two can't drift.Today's only production caller of
postis the MCP tool, an AGENT-origin post, and every value it can pass is already within these bounds. So nothing that works now changes.Tests
In
NotificationCenterTest:In
NotificationMcpToolProviderTest:MAX_LIST_LIMIT < NotificationCenter.MAX_ENTRIESis pinned directly (the review's nit). It is the invariant the list clamp depends on, and it now fails with a sentence rather than a count mismatch.Mutation-checked: removing each
require, storing the host message uncut, dropping the surrogate guard, not flattening the host label, and measuring it untrimmed each fail a named test. 420 tests acrossai.rever.boss.notifications.*andai.rever.boss.mcp.*pass.ktlintCheckanddetektare clean.🤖 Generated with Claude Code