You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The TTSR paragraph-repeat detector landed in #1326 (packages/coding-agent/src/core/extensions/builtin/ttsr/detectors/collapse-paragraphs.ts) matches paragraphs by exact content: two hashes (FNV-1a style + djb2) plus the UTF-16 length (isSameParagraph), counted over a 64-entry ring, threshold 3, paragraphs >= 64 chars / 24 word chars.
Because the match is exact, a near-repeat loop escapes it: a model that re-narrates the same paragraph while only a counter, timestamp, or clause order drifts ("Now writing step 3...", "Now writing step 4...") produces distinct hashes, so the repeat count never reaches 3 and the stream runs until the user aborts. Interleaved repeats (A B A B A B) are already covered because the ring is windowed, not consecutive.
Constraint on the fix
Loosening the match alone (whitespace/number masking before hashing) re-creates the false positive this detector was tuned to avoid: a legitimate list traversal that prints "processing item N" for N = 1..k collapses to one hash and would be interrupted as a loop. So a normalized match needs a progress gate as a precondition, e.g. treat a candidate repeat as a real loop only when no tool call was emitted between the repeats (the incident stream had 0 tool calls over 38K chars) or when tool arguments/checkpoints did not advance. Both halves belong in one change.
Evidence
Incident and replay analysis: sionicai session 01a06648 (2026-09-03), 38,193-char single text part, 7-paragraph cycle repeated ~9x, 0 tool calls; the exact-cycle case is what fix(ttsr): interrupt within-message paragraph repetition #1326 now catches.
Summary
The TTSR paragraph-repeat detector landed in #1326 (
packages/coding-agent/src/core/extensions/builtin/ttsr/detectors/collapse-paragraphs.ts) matches paragraphs by exact content: two hashes (FNV-1a style + djb2) plus the UTF-16 length (isSameParagraph), counted over a 64-entry ring, threshold 3, paragraphs >= 64 chars / 24 word chars.Because the match is exact, a near-repeat loop escapes it: a model that re-narrates the same paragraph while only a counter, timestamp, or clause order drifts ("Now writing step 3...", "Now writing step 4...") produces distinct hashes, so the repeat count never reaches 3 and the stream runs until the user aborts. Interleaved repeats (A B A B A B) are already covered because the ring is windowed, not consecutive.
Constraint on the fix
Loosening the match alone (whitespace/number masking before hashing) re-creates the false positive this detector was tuned to avoid: a legitimate list traversal that prints "processing item N" for N = 1..k collapses to one hash and would be interrupted as a loop. So a normalized match needs a progress gate as a precondition, e.g. treat a candidate repeat as a real loop only when no tool call was emitted between the repeats (the incident stream had 0 tool calls over 38K chars) or when tool arguments/checkpoints did not advance. Both halves belong in one change.
Evidence
collapse-paragraphs.tsisSameParagraph(hashA/hashB/utf16Length equality),PARAGRAPH_REPEAT_THRESHOLD = 3,PARAGRAPH_RING_CAPACITY = 64.Surfaced while answering a community question in the Ultraworkers Discord (#chat-ko, 2026-09-03).