Skip to content

Fix unclosed style marker crash - #364

Merged
benjaminking merged 4 commits into
mainfrom
fix-unclosed-style-marker-crash
Sep 14, 2026
Merged

benjaminking merged 4 commits into
mainfrom
fix-unclosed-style-marker-crash

Conversation

@benjaminking

@benjaminking benjaminking commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

This PR corrects a few different errors that could occur with marker placement, due to unmatched markers in the source, one of which was causing a crash, and the others leading to incorrect output:

  • An unclosed style marker in a non-verse paragraph would cause a crash
  • An unclosed style marker would cause the next occurring paragraph marker to be ignored
  • Only the first row of a verse range was being read
  • An element of type OTHER could cause markers to be placed out of order
  • Plus one corner case where end_note was behaving inconsistently in duplicated verses

This change is Reviewable

Ben King and others added 2 commits September 9, 2026 16:31
An unclosed character style is closed implicitly by the next paragraph
marker. end_char consumed tokens through state.index even on an implicit
close, so that paragraph marker was pulled into the block being closed and
dropped along with the removed style. In a non-verse paragraph it also left
the place markers handler holding a paragraph element marked for removal,
which satisfied the early-out guard but produced nothing to place, raising
IndexError.

- end_char only consumes tokens when the style is explicitly closed, as
  end_note and end_sidebar already did
- end_note mirrors start_note for duplicate verses, which end_char had been
  masking by consuming the end marker first
- the place markers early-out guard is derived from a single _is_placeable
  predicate so it cannot disagree with element collection, plus an explicit
  check for nothing left to place
- OTHER elements no longer count as content when locating end-of-verse
  paragraph markers, which was moving embeds past them
- TEXT elements are read in full: a verse range matched by several rows has
  one token per row, and reading only the first dropped the rest of the
  translation

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@Enkidu93 Enkidu93 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Enkidu93 reviewed 8 files and all commit messages, and made 3 comments.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on benjaminking and ddaspit).


tests/corpora/test_place_markers_usfm_update_block_handler.py line 732 at r1 (raw file):

def test_unclosed_style_marker_in_non_verse_paragraph() -> None:

I'm assuming that all these tests were previously failing since you've added them or are covering a portion of uncovered code? I'm just surprised that all of these would have failed.


tests/corpora/test_place_markers_usfm_update_block_handler.py line 873 at r1 (raw file):

def test_verse_range_matched_by_multiple_rows() -> None:
    # A verse range picks up one text token per matched row, so all of them have to be read.

I'm unclear on what the point of this test is exactly. The combination of the test name and the comment aren't making sense to me. Is this really just a test that verse ranges are handled properly? Or is the point that there's duplicate text?


tests/corpora/test_place_markers_usfm_update_block_handler.py line 804 at r1 (raw file):

def test_marker_behavior_disagrees_with_alignment_info() -> None:
    # The behaviors in the alignment info are supplied by the caller and can disagree with the

Are both necessary? Having looked into this while making this fix, do recall if these is a clear reason we would need the behavior specified in both places?

@Enkidu93 Enkidu93 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for digging into this, Ben!

@Enkidu93 made 1 comment.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on benjaminking and ddaspit).

@benjaminking benjaminking left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benjaminking reviewed 1 file and made 3 comments.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on ddaspit and Enkidu93).


tests/corpora/test_place_markers_usfm_update_block_handler.py line 732 at r1 (raw file):

Previously, Enkidu93 (Eli C. Lowry) wrote…

I'm assuming that all these tests were previously failing since you've added them or are covering a portion of uncovered code? I'm just surprised that all of these would have failed.

I don't think the marker placement code was ever tested for unmatched markers. I do believe all of these tests are covering behavior that was not tested previously (and I'm pretty sure these were all failing tests)


tests/corpora/test_place_markers_usfm_update_block_handler.py line 804 at r1 (raw file):

Previously, Enkidu93 (Eli C. Lowry) wrote…

Are both necessary? Having looked into this while making this fix, do recall if these is a clear reason we would need the behavior specified in both places?

I looked into it, and the behavior between the two is a little bit different, so we can't just use one or the other. I do, however, think this design is unintuitive, but I'll try to address that in a separate PR.


tests/corpora/test_place_markers_usfm_update_block_handler.py line 873 at r1 (raw file):

Previously, Enkidu93 (Eli C. Lowry) wrote…

I'm unclear on what the point of this test is exactly. The combination of the test name and the comment aren't making sense to me. Is this really just a test that verse ranges are handled properly? Or is the point that there's duplicate text?

The marker placement algorithm was only pulling out the first text row when it was reconstructing a verse range. It was essentially hard-coded for single verses.

@Enkidu93 Enkidu93 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Enkidu93 made 3 comments.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on benjaminking and ddaspit).


tests/corpora/test_place_markers_usfm_update_block_handler.py line 732 at r1 (raw file):

Previously, benjaminking (Ben King) wrote…

I don't think the marker placement code was ever tested for unmatched markers. I do believe all of these tests are covering behavior that was not tested previously (and I'm pretty sure these were all failing tests)

OK, great! Thanks for catching this. It's crazy how many tests there are and yet still things like this could be broken.


tests/corpora/test_place_markers_usfm_update_block_handler.py line 804 at r1 (raw file):

Previously, benjaminking (Ben King) wrote…

I looked into it, and the behavior between the two is a little bit different, so we can't just use one or the other. I do, however, think this design is unintuitive, but I'll try to address that in a separate PR.

OK, feel free to just create an issue as well to track it as future works with your conclusions - I don't want to keep you from other things.


tests/corpora/test_place_markers_usfm_update_block_handler.py line 873 at r1 (raw file):

Previously, benjaminking (Ben King) wrote…

The marker placement algorithm was only pulling out the first text row when it was reconstructing a verse range. It was essentially hard-coded for single verses.

I see, OK. I'm still not 100% following the comment or the test name. The 'verse range is matched by multiple rows'? The 'verse range picks up a text token per matched row'? Maybe I'm not reading them correctly but these don't make sense to me. If the important part is just that it's testing a verse range, we could probably just keep it at that. If it's important that the same tokens occur across parts of the range, then maybe mention that in the comment? Or if you feel that these are already clear and relevant, could you explain what they mean to me? 😆

@benjaminking benjaminking left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benjaminking made 1 comment.
Reviewable status: 7 of 8 files reviewed, 3 unresolved discussions (waiting on ddaspit and Enkidu93).


tests/corpora/test_place_markers_usfm_update_block_handler.py line 873 at r1 (raw file):

Previously, Enkidu93 (Eli C. Lowry) wrote…

I see, OK. I'm still not 100% following the comment or the test name. The 'verse range is matched by multiple rows'? The 'verse range picks up a text token per matched row'? Maybe I'm not reading them correctly but these don't make sense to me. If the important part is just that it's testing a verse range, we could probably just keep it at that. If it's important that the same tokens occur across parts of the range, then maybe mention that in the comment? Or if you feel that these are already clear and relevant, could you explain what they mean to me? 😆

I have updated the name of the test as well as the comments. It was definitely a bit of the Claude code idiolect.

@codecov-commenter

codecov-commenter commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.94737% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.12%. Comparing base (b09498c) to head (d1932d7).

Files with missing lines Patch % Lines
...corpora/place_markers_usfm_update_block_handler.py 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #364      +/-   ##
==========================================
+ Coverage   92.10%   92.12%   +0.02%     
==========================================
  Files         389      390       +1     
  Lines       24559    24635      +76     
==========================================
+ Hits        22620    22695      +75     
- Misses       1939     1940       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Enkidu93 Enkidu93 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@Enkidu93 reviewed all commit messages, made 2 comments, and resolved 1 discussion.
Reviewable status: 7 of 8 files reviewed, 2 unresolved discussions (waiting on benjaminking and ddaspit).


tests/corpora/test_place_markers_usfm_update_block_handler.py line 873 at r1 (raw file):

Previously, benjaminking (Ben King) wrote…

I have updated the name of the test as well as the comments. It was definitely a bit of the Claude code idiolect.

Thank you - much better 😁

@benjaminking
benjaminking merged commit 29088f8 into main Sep 14, 2026
17 of 18 checks passed
@benjaminking
benjaminking deleted the fix-unclosed-style-marker-crash branch September 14, 2026 21:35
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.

3 participants