diff --git a/strix/tools/finish/tool.py b/strix/tools/finish/tool.py index 9484a4539..dd2bb1cec 100644 --- a/strix/tools/finish/tool.py +++ b/strix/tools/finish/tool.py @@ -33,15 +33,26 @@ def _do_finish( ), } + report_sections = ( + ("Executive summary", executive_summary, "Business-level summary for leadership."), + ("Methodology", methodology, "Frameworks, scope, and approach."), + ( + "Technical analysis", + technical_analysis, + "Consolidated findings + systemic themes.", + ), + ( + "Recommendations", + recommendations, + "Prioritized, actionable remediation.", + ), + ) errors: list[str] = [] - if not executive_summary.strip(): - errors.append("Executive summary cannot be empty") - if not methodology.strip(): - errors.append("Methodology cannot be empty") - if not technical_analysis.strip(): - errors.append("Technical analysis cannot be empty") - if not recommendations.strip(): - errors.append("Recommendations cannot be empty") + for section_name, section_text, schema_description in report_sections: + if not section_text.strip(): + errors.append(f"{section_name} cannot be empty") + elif " ".join(section_text.split()) == schema_description: + errors.append(f"{section_name} cannot be a schema description placeholder") if errors: return {"success": False, "error": "Validation failed", "errors": errors} diff --git a/tests/test_finish_coverage_gate.py b/tests/test_finish_coverage_gate.py index 52a19a7d9..3ad69e61f 100644 --- a/tests/test_finish_coverage_gate.py +++ b/tests/test_finish_coverage_gate.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING import pytest from strix.tools.coverage.tools import _record_impl, hydrate_coverage_from_disk -from strix.tools.finish.tool import _coverage_summary +from strix.tools.finish.tool import _coverage_summary, _do_finish, finish_scan if TYPE_CHECKING: @@ -63,3 +63,28 @@ def test_an_empty_ledger_still_warns_first() -> None: assert summary["coverage_recorded"] == 0 assert "No coverage was recorded" in summary["coverage_warning"] + + +@pytest.mark.parametrize( + "field_name", + [ + "executive_summary", + "methodology", + "technical_analysis", + "recommendations", + ], +) +def test_finish_rejects_schema_description_placeholders(field_name: str) -> None: + report_fields = { + "executive_summary": "No material exposure was confirmed.", + "methodology": "The scoped paths were reviewed and exercised.", + "technical_analysis": "Observed controls held under the tested cases.", + "recommendations": "Retest after material changes.", + } + placeholder = finish_scan.params_json_schema["properties"][field_name]["description"] + report_fields[field_name] = f" \n{placeholder}\t" + + result = _do_finish(parent_id=None, agent_graph={}, **report_fields) + + assert result["success"] is False + assert "placeholder" in " ".join(result["errors"]).lower()