diff --git a/scripts/pr_review.py b/scripts/pr_review.py
index 636708668..dce7f1ef4 100755
--- a/scripts/pr_review.py
+++ b/scripts/pr_review.py
@@ -2941,7 +2941,7 @@ def heading_of(block: str) -> str:
# `(.*?) ` lazily pairs each open with the *next* close, which is the innermost one once a shape nests, silently losing everything the outer wrapper still carries after it.
# CodeRabbit's outside-diff section does exactly that: a file wrapper nested inside the section heading, itself wrapping a per-finding "Prompt for AI Agents" block three levels deep.
DETAILS_TAG = re.compile(
- r"]*|/)?>| "
+ r"]*|/)?>| "
r"|(?\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]*|/)>",
re.IGNORECASE,
)
diff --git a/tests/test_pr_review.py b/tests/test_pr_review.py
index ff5d835bc..e0e2457ce 100755
--- a/tests/test_pr_review.py
+++ b/tests/test_pr_review.py
@@ -60,7 +60,7 @@ def flowed_text(text: str) -> str:
# The shape 28 of the 333 measured bodies carry: an overview, and no count of what was read.
# A body of no text at all is not one of the shapes, and the reader now says so, correctly.
OVERVIEW = "## Pull request overview\n\nThe change is narrow.\n"
-# The wall-clock ceiling the two backtracking cases read, stated once so they cannot drift apart.
+# The wall-clock ceiling the backtracking cases read, stated once so they cannot drift apart.
# Each case sizes its own input so that a regression finishes and trips this rather than running long enough to hang the suite.
# Three seconds against readings of microseconds and milliseconds, which is the margin that makes a loaded runner a non-issue rather than the clock being trusted.
BACKTRACK_BOUND = 3.0
@@ -3347,6 +3347,7 @@ def test_a_tag_is_bounded_by_html_s_own_syntax(self) -> None:
"""A tail reaching another line or another tag swallowed the opener or heading after it."""
cases = {
"": [""],
+ "": [""],
" ": [],
" ": [],
"": [],
@@ -3359,6 +3360,16 @@ def test_a_tag_is_bounded_by_html_s_own_syntax(self) -> None:
found = [m.group() for m in pr_review.DETAILS_TAG.finditer(text)]
self.assertEqual(tags, found)
+ def test_a_body_of_unclosed_openers_is_read_in_linear_time(self) -> None:
+ """An opener with no closing `>` ends its scan at the next `<`, so a body of such openers reads in linear time."""
+ body = " None:
"""The section readers pair on the same tags, so their regions end where HTML ends them."""
body = "\na\n \nb\n\nc\n \n"