feat(open-swe): collapse oversized finding ranges to start line (#1263)

Anchoring a finding to a 25-line range (e.g. an entire function) makes
GitHub render the whole block as context above the comment, burying the
review text. Cap ranges at 10 lines and collapse to the start line when
exceeded; small ranges (≤10 lines) still render as a multi-line anchor.
Updated the reviewer prompt to anchor tightly rather than range-select
the whole function.
This commit is contained in:
Johannes du Plessis 2026-05-07 19:59:25 -07:00 • committed by GitHub
parent 0212a60f10
commit 089db06135
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 98 additions and 3 deletions

View file

@ -109,8 +109,12 @@ Cloning is optional — for most PRs the diff alone is enough.
issue worth surfacing; `low` = small nit; `informational` = FYI / context,
not a flaw. Inflated severities erode trust — be honest.
- `category`: e.g. `correctness`, `security`, `perf`, `style`, `flag`.
- `file`, `start_line`, `end_line`: anchor inside the PR diff. Use a range
when the issue spans multiple lines (e.g. an entire function).
- `file`, `start_line`, `end_line`: anchor inside the PR diff. Keep
ranges tight — anchor to the few lines that actually matter (the call
site, the signature line, the conditional). **Do not range-select an
entire function**; GitHub renders the full range as context above the
comment and buries the point. Ranges over ~10 lines are auto-collapsed
to the start line.
- `description`: what's wrong, in 1–4 sentences. Markdown is fine.
- `suggestion`: **only** include for small, obvious fixes that fit in 4
lines or fewer — a one-liner rename, a missing guard, a typo, a flipped

View file

@ -29,6 +29,13 @@ REVIEWER_THREAD_KIND = "reviewer"
# longer suggestions; the description still gets posted on its own.
MAX_SUGGESTION_LINES = 4
# Anchoring a finding to a giant range (e.g. an entire function) makes the
# GitHub comment dump dozens of lines of context above the actual review
# text, which buries the point. When the range exceeds this, collapse it to
# the first line — the comment still anchors near the issue without showing
# the whole block. ~5-line ranges are still useful, so the cap is generous.
MAX_FINDING_RANGE_LINES = 10
def clip_suggestion(suggestion: str | None) -> tuple[str | None, bool]:
"""Return (suggestion_or_none, was_dropped). Drops if over the line cap."""
@ -39,6 +46,18 @@ def clip_suggestion(suggestion: str | None) -> tuple[str | None, bool]:
return suggestion, False
def clip_finding_range(
start_line: int | None,
end_line: int | None,
) -> tuple[int | None, int | None, bool]:
"""Return (start, end, was_collapsed). Collapses end→start if over cap."""
if start_line is None or end_line is None:
return start_line, end_line, False
if end_line - start_line + 1 > MAX_FINDING_RANGE_LINES:
return start_line, start_line, True
return start_line, end_line, False
Severity = Literal["informational", "low", "medium", "high", "critical"]
FindingStatus = Literal["open", "resolved", "dismissed"]
DiffSide = Literal["LEFT", "RIGHT"]

View file

@ -9,11 +9,13 @@ from langgraph.config import get_config
from ..reviewer_diff import is_range_in_diff
from ..reviewer_findings import (
MAX_FINDING_RANGE_LINES,
MAX_SUGGESTION_LINES,
DiffSide,
Finding,
Severity,
append_finding,
clip_finding_range,
clip_suggestion,
get_thread_id_from_runtime,
new_finding,
@ -56,6 +58,11 @@ def add_finding(
``end_line`` for single-line findings; less than ``end_line`` for
ranges. Omit (with ``end_line``) for file-level findings.
end_line: 1-based end line, inclusive. Defaults to ``start_line``.
Keep ranges tight — anchor to the few lines that actually matter
for the comment. Ranges over 10 lines are auto-collapsed to a
single line because GitHub renders the full range as context
above the comment, which buries the point. Don't anchor to an
entire function; pick the call site or signature line instead.
suggestion: Replacement text for ``start_line..end_line``. When set,
the published GitHub comment includes a ```suggestion``` block so
the user can click "Commit suggestion". **Only set this for small,
@ -99,6 +106,8 @@ def add_finding(
),
}
start_line, end_line, range_collapsed = clip_finding_range(start_line, end_line)
diff_hunk: str | None = None
if isinstance(diff_text, str) and diff_text:
from ..reviewer_diff import extract_diff_hunk
@ -130,6 +139,14 @@ def add_finding(
"dropped — the finding was recorded with description only. Only "
"include `suggestion` for small, obvious fixes."
)
if range_collapsed:
result["range_collapsed"] = True
result["range_warning"] = (
f"Range exceeded the {MAX_FINDING_RANGE_LINES}-line cap and was "
f"collapsed to a single line ({start_line}). Anchor findings to "
"the most relevant line — GitHub renders large ranges as walls "
"of context that bury the comment."
)
return result

View file

@ -16,7 +16,7 @@ def _config(**configurable_overrides: Any) -> dict[str, Any]:
"thread_id": "tid-1",
"head_sha": "sha-head",
"diff_text": "",
"diff_line_set": {"foo.py": [10, 11, 12]},
"diff_line_set": {"foo.py": list(range(10, 41))},
},
"metadata": {},
}
@ -175,6 +175,61 @@ def test_add_finding_keeps_short_suggestion() -> None:
assert captured[0]["suggestion"] == short_suggestion
def test_add_finding_collapses_oversized_range() -> None:
captured: list[Any] = []
async def fake_append(thread_id: str, finding: Any) -> Any:
captured.append(finding)
return finding
with (
patch("agent.tools.add_finding.get_config", return_value=_config()),
patch("agent.tools.add_finding.get_thread_id_from_runtime", return_value="tid-1"),
patch("agent.tools.add_finding.append_finding", side_effect=fake_append),
):
result = add_finding(
severity="medium",
category="correctness",
file="foo.py",
description="big function",
start_line=15,
end_line=40, # 26 lines — well over the cap
)
assert result["success"] is True
assert result.get("range_collapsed") is True
assert "range_warning" in result
assert captured[0]["start_line"] == 15
assert captured[0]["end_line"] == 15
def test_add_finding_keeps_small_range() -> None:
captured: list[Any] = []
async def fake_append(thread_id: str, finding: Any) -> Any:
captured.append(finding)
return finding
with (
patch("agent.tools.add_finding.get_config", return_value=_config()),
patch("agent.tools.add_finding.get_thread_id_from_runtime", return_value="tid-1"),
patch("agent.tools.add_finding.append_finding", side_effect=fake_append),
):
result = add_finding(
severity="low",
category="style",
file="foo.py",
description="five lines",
start_line=15,
end_line=19, # 5 lines — within the cap
)
assert result["success"] is True
assert "range_collapsed" not in result
assert captured[0]["start_line"] == 15
assert captured[0]["end_line"] == 19
def test_update_finding_rejects_long_suggestion_without_clobbering() -> None:
"""Over-cap suggestion alongside other fields: drop suggestion, keep the rest."""
captured: list[Any] = []