fix(notify): stop cutting "what changed" mid-word - #552
Merged
Conversation
Smana
force-pushed
the
fix/what-changed-word-boundary
branch
from
August 24, 2026 13:28
a59d8c8 to
1757ca8
Compare
The field was capped at 200 runes with the generic truncate helper, so a live card ended "…consistent with Coder provisioning workspaces outside Argo/Fl…". That reads as a broken renderer rather than an intentional elision, and 200 was tighter than what models actually put in change_ref: some cards carry a revision range, others a sentence explaining the change, and the explanatory form is the more useful one. truncateWords backs the cut off to the last space, and the field rises to 600 runes. The cap stays enforced — a tail with no space in its final half is one long token (a sha, a URL) and still gets the hard cut rather than losing half its value. The scan starts at the FIRST DROPPED rune, not the last kept one, which is what makes the word-boundary case ordinary rather than special: when the cap lands exactly on a space the prefix already ends on a boundary, and starting there finds it instead of scanning past a whole word that fitted. The live fixture is that case — at 600 it cuts right after "the", and the first attempt threw that word away. The value is capped BEFORE escaping, reversing this file's other two truncate sites deliberately. Those cap at 2900, a last-resort guard where it does not matter that "&" costs 5 of the budget. This cap fires routinely, so measuring escaped runes would hand a change_ref describing "1.14.2 -> 1.15.0 && <prod>" a materially smaller allowance than one without meta characters — and capping the source also means truncateWords only ever sees real prose, so no cut can sever an entity. n <= 1 is guarded: no caller passes it today, but the doc invites reuse and a caller deriving n from a remaining budget reaches 0 by subtraction, where r[:n-1] panicked. A panic in the notifier takes down the card for an incident already in progress. The guard is measured, not asserted: TestTruncateWords is the unit table covering the hard cut, the exact boundary and the degenerate caps, and the card-level test now measures the rendered FIELD. An earlier version asserted len(blocksText(...)) > 260, which the header, verdict and footer satisfy on their own — it passed with the cap set to 60, so the half of the guard meant to pin the raised cap pinned nothing. Mutation-tested against caps of 60 and 200 and against reverting to plain truncate. internal/thread.truncateWords is the byte-budget twin of this function; both doc comments now say so.
Smana
force-pushed
the
fix/what-changed-word-boundary
branch
from
August 24, 2026 16:36
21bb70f to
4bc2bb6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The What changed metadata field was capped at 200 runes with the generic
truncatehelper, so a live card ended:Two things were wrong at once.
The cut landed inside a word. That reads as a broken renderer rather than an intentional elision.
200 was tighter than what models actually put in
change_ref. Some cards carry a revision range; others carry a sentence explaining the change — and the explanatory form is the more useful of the two.The fix
truncateWordsbacks the cut off to the last space, and the field rises to 600 runes — enough to hold an explanatorychange_ref, still leaving the two-column metadata grid readable, and well inside the 2000-rune per-field capadd()already applies.The cap stays enforced. The back-off only searches the final half of the allowance: a tail with no space in it is one long token — a sha, a URL — and that still gets the hard cut rather than losing half its value.
truncateis untouched and still the right helper for single-token values and hard protocol limits; the doc comment ontruncateWordssays which to reach for.Guard
TestSlackWhatChangedTruncatesOnWordBoundaryfeeds the live over-longchange_refand asserts both halves: the rendered tail before the ellipsis is a whole word of the source, and the output carries more than the old 200-rune cap allowed.Fixes F7.