Skip to content

AIS-5214: Clamp ending overlap in add/remove fold - #3

Merged
saghen merged 2 commits into
SpeechifyInc:masterfrom
nguyenlc1993:nicholas/ais-5214-string-tracker-overlap-fold-drops-a-duplicated-char-from-the
Sep 17, 2026
Merged

saghen merged 2 commits into
SpeechifyInc:masterfrom
nguyenlc1993:nicholas/ais-5214-string-tracker-overlap-fold-drops-a-duplicated-char-from-the

Conversation

@nguyenlc1993

Copy link
Copy Markdown
Contributor

Summary

  • Fixes the add/remove overlap fold in concatChanges (src/helpers.ts), which could drop a character from the change list.
  • beginningOverlap and endingOverlap were computed independently. When the boundary character repeats (' ' vs ' '), the same character counted toward both, so getNonOverlap trimmed it twice.
  • endingOverlap is now clamped to minLength - beginningOverlap where it is computed. The trailing common-suffix push already applied this clamp; the trim did not.
  • Adds 3 regression cases in tests/index.test.ts. Each pins the exact getChanges() output and runs assertValidTracker.

Repro (before this change)

case changes rebuilt from changes actual
' 0'.remove(2,3).add(2,'😊 ').remove(0,5) [' ', [1,'0']] original ' 0' ' 0'
'aa'.add(0,'aaa').remove(3,5) ['aa'] modified 'aa' 'aaa'
'aab'.add(0,'ab').remove(2,5) ['ab'] original 'ab' 'aab'

Impact

  • Both sides of the change list: when the add is longer than the remove, the modified side breaks too, not only the original side.
  • audio-server: its engine cache rehydrates trackers from getChangeChunks(). Chunk lengths are derived from the change list, so index mapping on those rehydrated trackers can be skewed.
  • Existing cache entries: entries written by older versions keep their bad chunks until they are evicted.

Test plan

  • The new cases fail before the fix, on the exact broken arrays shown above
  • npm test: 180/180 pass
  • npm run fuzz: 5000 iterations pass
  • npm run build succeeds

Follow-up

  • Release v1.4.3 after merge.
  • Go port (ai-platform tts-service/pkg/stringtracker): apply the same clamp, add the 3 cases as fixtures, regenerate the golden oracle, and bump @speechifyinc/string-tracker in audio-server.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DZPwQoG2HtFHPnTDvsncrR

nguyenlc1993 and others added 2 commits September 11, 2026 17:05
beginningOverlap and endingOverlap were computed independently, so a
repeated boundary char (' ' vs '  ') counted toward both and
getNonOverlap over-trimmed, dropping a char from the change list's
original or modified projection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DZPwQoG2HtFHPnTDvsncrR
Constructing trackers in the it.each table ran them at file load, so a
throw would fail the whole file instead of one case. Also swap the emoji
repro for ASCII: surrogate pairs play no part in the bug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DZPwQoG2HtFHPnTDvsncrR
@saghen
saghen merged commit 337d922 into SpeechifyInc:master Sep 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants