Skip to content

Fix 20 bugs found in a bug hunt - #14203

Merged
niksedk merged 1 commit into
mainfrom
claude/bug-hunt-18
Aug 27, 2026
Merged

Fix 20 bugs found in a bug hunt#14203
niksedk merged 1 commit into
mainfrom
claude/bug-hunt-18

Conversation

@niksedk

@niksedk niksedk commented Aug 27, 2026

Copy link
Copy Markdown
Member

Sweep 18. The verified backlog from earlier sweeps' agent reports, plus fresh passes over the Tools dialogs and the remaining libse areas.

Suites: libse 1635 / libuilogic 708 / seconv 422 / UI 4159.

Wrong results

  • Apply minimum gap could write a negative duration. Pulling a line's end back to next.Start - gap can land before that line's own start when the line is short and the next one starts soon after. It was applied anyway and counted as a fix, so the bad duration reached the grid and the saved file.
  • Adjust durations (Fixed and Percent) capped the end flat at the next line's start, so two rows sharing a start time produced a zero-duration line and out-of-order rows a negative one. The Seconds branch in the same file already floors its result, and libse's SetFixedDuration / AdjustDisplayTimeUsingPercent leave the minimum gap — so the same operation behaved differently in Batch convert.
  • Sort by: removing the last criterion left the previous sorted order in the preview, and OK applied it. ApplySorting already returns the rows unsorted for an empty criteria list; an early return in UpdatePreview stopped it ever running. ClearSortCriteria repopulates by hand for exactly this reason.
  • Convert actors applied whatever the 500 ms preview timer last computed, so pressing OK right after changing the from/to type, colour or casing applied the old settings while SaveSettings persisted the new ones — and before the first tick it applied nothing at all. Five sibling dialogs in that folder already refresh on OK and carry the comment.
  • Merge lines with same time codes produced a double dash. The default dialog style tested StartsWith("- ") for an existing dash while its sibling case tests "-", so "-Hello" came back as "- -Hello". Fixed in both the libse helper and the live copy in the dialog.
  • FirstOnOrAfter/FirstOnOrBefore ran their tolerance probe even on an exact BinarySearch hit and returned the neighbour on the wrong side. A cue already snapped onto a shot change asked for "the next shot change" and got one 20 ms earlier. The generic overloads in the same file only rewrite the index when the search missed.
  • StrippableText took the text before [ as [0, idx - 1), dropping the very character the test inspects: "Yes.[Motor roaring] hello." saw "Yes" instead of "Yes." and left hello lowercase. The documented "I [Motor roaring]" case is unaffected.
  • The word-split exception tested a lowercase 'a' that the enclosing "starts with its own uppercase" guard had already excluded, so "Acat" was never split while the 'I' sibling ("Iam") worked.
  • Google Lens always sent LocaleContext.Language = "en". The chosen language reaches the Accept-Language header but not the protobuf request, which read a config that nothing ever populates (new Lens() takes no config and UpdateOptions is never called).
  • The grid's ASSA highlighter treated \clip as a colour tag — the test excluded a digit after c but not a letter — so a {\clip(...)} block cleared the colour state and the rest of the line lost its colour in the grid while libass and SE's own preview kept it.
  • Bridge gaps wrote fractional milliseconds; now rounded at the producer (Duration can become inconsistent by 1 ms while editing #14056).
  • On Windows, an association the user reassigned in Explorer still reported SE as the default: a non-matching UserChoice fell through to the legacy Software\Classes key that SE itself had written.

Never displayed, never persisted

  • 15 buttons set an icon and then bound ContentProperty over it, so the icon never rendered and — since the label starts empty — the buttons had no accessible name either. One did it in the opposite order and lost the bound text instead. UiUtil.WithIconLeftBindText exists for this and is used correctly by five sibling dialogs.
  • The three download dialogs set Error on every failure path but bound only ProgressText, so the user saw a generic "Download failed" while the real cause went to a property no window rendered. Three other download windows bind it.
  • The auto-cast speakers dialog restored a window position that nothing ever saved.
  • Accepting the IndexTTS 2.5 licence was not persisted immediately, so it could be asked again after an abnormal exit. The voice-cloning consent dialog saves on accept.
  • The speech-history dialog's play buttons opened disabled until an unrelated watchdog tick enabled them.
  • Chatterbox's install-folder path was added one grid row below its own label.

Leaks

Cancelling an engine download stranded the partial .7z — up to ~1.5 GB for Faster-Whisper-XXL / WhisperX — in the engine folder, since only the success path deleted it.

A fix the tests corrected

My first ApplyMinGap patch used the same newDuration > SubtitleMinimumDisplayMilliseconds guard the batch converter uses. That broke two existing tests — correctly: the default minimum is 1000 ms, so the guard skipped ordinary shortening and defeated the feature. Those tests encode the dialog's real contract. The shipped fix guards only against a non-positive duration, which is the actual defect, and a new test pins it (it fails without the fix).

Flagged but not fixed

Two CEA-708 / MacCaption findings need someone who can validate against real .mcc files from other tools, so I have left them alone rather than change what SE writes on a static-analysis argument:

  • Smpte291M never skips the optional CDP time_code_section (id 0x71) when CaptionDistributionPacketTimeCodeAdded is set — the reader hardcodes the cc-data offset, while the sibling ServiceInfoAdded flag is honoured. Files from other tools would decode to nothing.
  • The SMPTE 291M ancillary checksum is emitted as the literal 0xbb. The arithmetic suggests it should be 0xB4, and 0xBB happens to equal 0x61 + 0x01 + 0x59 where 0x59 is a stale data count the writer later overwrites with 82 — so it looks like a genuine leftover. SE re-reads its own files because the reader never verifies the checksum.

The mpv event-loop test still fails intermittently here; per the previous PR that is a worktree artifact, not a source issue, and nothing in this PR touches the video players.

🤖 Generated with Claude Code

Backlog from the previous sweeps' agent reports, plus fresh passes over the
Tools dialogs and the remaining libse areas.

Wrong results
- Apply minimum gap could pull a line's end BEFORE its own start (a short
  line followed closely by the next), writing a negative duration to the
  grid and the saved file while counting it as a fix.
- Adjust durations (Fixed and Percent) capped the end flat at the next
  line's start, so two rows sharing a start time produced a zero-duration
  line and out-of-order rows a negative one. Now leaves the minimum gap and
  floors the duration, as libse and the batch path already do.
- Sort by: removing the last criterion left the previous sorted order in the
  preview, and OK applied it. ApplySorting already handles an empty list;
  the early return stopped it running.
- Convert actors applied whatever the 500 ms preview timer last computed, so
  OK right after a settings change used the old settings while saving the
  new ones. Every sibling dialog in that folder already refreshes on OK.
- Merge lines with same time codes tested "- " for an existing dash in the
  default dialog style, so "-Hello" became "- -Hello". Its sibling case in
  the same switch tests "-".
- FirstOnOrAfter/FirstOnOrBefore ran their tolerance probe even on an exact
  BinarySearch hit and returned the neighbour on the wrong side - a cue
  snapped onto a shot change found the shot change 20 ms earlier.
- StrippableText took the text before "[" as [0, idx - 1), dropping the very
  character the test looks at: "Yes.[Motor roaring] hello." left "hello"
  lowercase.
- The word-split exception tested a lowercase 'a' that the enclosing
  "starts with its own uppercase" guard had already excluded, so "Acat" was
  never split while "Iam" was.
- Google Lens sent LocaleContext.Language "en" whatever OCR language was
  picked: the code reached the Accept-Language header but not the protobuf
  request, and nothing ever populates the config it read instead.
- The grid's ASSA highlighter treated "\clip" as a colour tag (the test
  excluded a digit but not a letter), so the rest of the line lost its
  colour.
- Bridge gaps wrote fractional milliseconds; rounded at the producer
  (#14056).
- On Windows, a file association reassigned by the user in Explorer still
  reported SE as the default, because a non-matching UserChoice fell through
  to the legacy key SE had written itself.

Never displayed / never persisted
- 15 buttons set an icon and then bound ContentProperty over it, so the icon
  never rendered and the buttons had no accessible name; one did it in the
  other order and lost the bound text instead.
- The three download dialogs set Error on every failure path but bound only
  ProgressText, so the user saw "Download failed" and never the reason.
- The auto-cast speakers dialog restored a window position that nothing ever
  saved.
- Accepting the IndexTTS 2.5 licence was not persisted immediately, so it
  could be asked again.
- The speech history dialog's play buttons opened disabled until an
  unrelated watchdog tick enabled them.
- Chatterbox's install-folder path was added one grid row below its label.

Leaks
- Cancelling an engine download stranded the partial .7z (up to ~1.5 GB) in
  the engine folder; only the success path deleted it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@niksedk
niksedk merged commit f8a0250 into main Aug 27, 2026
1 check passed
@niksedk
niksedk deleted the claude/bug-hunt-18 branch August 27, 2026 23:22
@niksedk niksedk mentioned this pull request Aug 28, 2026
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.

1 participant