Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions strix/interface/tui/renderers/finish_renderer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from typing import Any, ClassVar

from rich.text import Text
Expand All @@ -10,6 +11,29 @@
FIELD_STYLE = "bold #4ade80"


def _strip_leading_heading(value: str, section: str) -> str:
"""Drop a leading markdown heading that just repeats the section label.

The finish_scan tool prompts the model to write markdown in every field, and
the models routinely open each with a ``# <Section>`` heading (e.g.
``# Executive Summary``). This renderer also prints its own styled section
label above the value, so that heading renders twice. Strip a leading
``#``-heading whose text matches this section (case-insensitively) so the
label isn't duplicated; leave all other content — including headings that
say something else — untouched. Also matches the closing-``#`` ATX form
(``# Executive Summary #``). If stripping the heading would leave nothing
(the field was ONLY the heading, e.g. ``# Recommendations\n``), keep the
original rather than render the field's sole content as blank.
"""
stripped = value.lstrip()
pattern = rf"^#{{1,6}}\s+{re.escape(section)}(?:\s+#+)?\s*\n+"
m = re.match(pattern, stripped, flags=re.IGNORECASE)
if not m:
return value
remainder = stripped[m.end() :]
return remainder if remainder.strip() else value


@register_tool_renderer
class FinishScanRenderer(BaseToolRenderer):
tool_name: ClassVar[str] = "finish_scan"
Expand All @@ -32,25 +56,25 @@ def render(cls, tool_data: dict[str, Any]) -> Static:
text.append("\n\n")
text.append("Executive Summary", style=FIELD_STYLE)
text.append("\n")
text.append(executive_summary)
text.append(_strip_leading_heading(executive_summary, "Executive Summary"))

if methodology:
text.append("\n\n")
text.append("Methodology", style=FIELD_STYLE)
text.append("\n")
text.append(methodology)
text.append(_strip_leading_heading(methodology, "Methodology"))

if technical_analysis:
text.append("\n\n")
text.append("Technical Analysis", style=FIELD_STYLE)
text.append("\n")
text.append(technical_analysis)
text.append(_strip_leading_heading(technical_analysis, "Technical Analysis"))

if recommendations:
text.append("\n\n")
text.append("Recommendations", style=FIELD_STYLE)
text.append("\n")
text.append(recommendations)
text.append(_strip_leading_heading(recommendations, "Recommendations"))

if not (executive_summary or methodology or technical_analysis or recommendations):
text.append("\n ")
Expand Down
77 changes: 77 additions & 0 deletions tests/test_finish_renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""Tests for the finish_scan TUI renderer (section-heading de-duplication)."""

from __future__ import annotations

from rich.text import Text

from strix.interface.tui.renderers.finish_renderer import (
FinishScanRenderer,
_strip_leading_heading,
)


def _plain(static: object) -> str:
content = static.content # type: ignore[attr-defined]
return content.plain if isinstance(content, Text) else str(content)


def _render(**args: str) -> str:
return _plain(FinishScanRenderer.render({"status": "completed", "args": args}))


# --- _strip_leading_heading -------------------------------------------------


def test_strips_matching_leading_heading() -> None:
assert _strip_leading_heading("# Executive Summary\n\nBody", "Executive Summary") == "Body"
assert _strip_leading_heading("## Methodology\nSteps", "Methodology") == "Steps"


def test_strip_is_case_and_whitespace_insensitive() -> None:
assert _strip_leading_heading("# technical analysis \n\nX", "Technical Analysis") == "X"


def test_keeps_a_different_leading_heading() -> None:
# A heading that isn't the section label is real content — leave it.
val = "# Findings Overview\nY"
assert _strip_leading_heading(val, "Executive Summary") == val


def test_keeps_body_when_no_heading() -> None:
assert _strip_leading_heading("No heading here", "Methodology") == "No heading here"


def test_keeps_lone_heading_no_trailing_newline() -> None:
# No trailing newline => doesn't match the pattern; kept.
assert _strip_leading_heading("# Recommendations", "Recommendations") == "# Recommendations"


def test_keeps_lone_heading_with_trailing_newline() -> None:
# Trailing newline DOES match, but stripping leaves nothing — the field's
# only content would vanish. Keep the original (Greptile P1).
assert _strip_leading_heading("# Recommendations\n", "Recommendations") == "# Recommendations\n"
assert _strip_leading_heading("## Methodology\n\n", "Methodology") == "## Methodology\n\n"


def test_strips_closing_atx_heading() -> None:
# Closed-ATX form `# Section #` must also dedupe (Greptile P2).
assert _strip_leading_heading("# Executive Summary #\n\nBody", "Executive Summary") == "Body"


# --- end-to-end render ------------------------------------------------------


def test_section_heading_rendered_once_not_twice() -> None:
# The model is prompted to write markdown and routinely opens each field
# with a `# <Section>` heading; the renderer also prints a styled label.
# Regression: both showed, doubling the heading. Now the field's leading
# heading is stripped so "Executive Summary" appears exactly once.
out = _render(executive_summary="# Executive Summary\n\nThe app is sound.")
assert out.count("Executive Summary") == 1
assert "The app is sound." in out


def test_render_preserves_body_without_heading() -> None:
out = _render(methodology="White-box review of the diff.")
assert "Methodology" in out
assert "White-box review of the diff." in out