fix(release): require visual selection evidence

This commit is contained in:
Pulse Test 2026-08-28 20:11:04 +01:00
parent 1d15ab60a3
commit a1ae0fa07f
8 changed files with 77 additions and 27 deletions

View file

@ -131,11 +131,12 @@ def _state(value: Any, field: str) -> dict[str, Any]:
def validate_plan(raw: Any) -> dict[str, Any]:
if not isinstance(raw, dict):
raise PlanError("visual plan must be a JSON object")
unknown = set(raw) - {"schema_version", "captures"}
unknown = set(raw) - {"schema_version", "decision", "captures"}
if unknown:
raise PlanError(f"visual plan has unsupported fields: {', '.join(sorted(unknown))}")
if raw.get("schema_version") != 1:
raise PlanError("visual plan schema_version must be 1")
decision = _text(raw.get("decision"), "visual plan decision", maximum=500)
captures = raw.get("captures")
if not isinstance(captures, list) or len(captures) > MAX_CAPTURES:
raise PlanError(f"visual plan captures must be a list with at most {MAX_CAPTURES} entries")
@ -191,7 +192,11 @@ def validate_plan(raw: Any) -> dict[str, Any]:
"after": _state(capture.get("after"), f"{field}.after"),
}
)
return {"schema_version": 1, "captures": normalized_captures}
return {
"schema_version": 1,
"decision": decision,
"captures": normalized_captures,
}
def load_plan(path: str) -> dict[str, Any]:
@ -273,9 +278,10 @@ def json_schema() -> dict[str, Any]:
return {
"type": "object",
"additionalProperties": False,
"required": ["schema_version", "captures"],
"required": ["schema_version", "decision", "captures"],
"properties": {
"schema_version": {"const": 1, "type": "integer"},
"decision": {"type": "string", "minLength": 1, "maxLength": 500},
"captures": {
"type": "array",
"maxItems": MAX_CAPTURES,

View file

@ -22,6 +22,7 @@ RENDERER_SPEC.loader.exec_module(renderer)
def valid_plan():
return {
"schema_version": 1,
"decision": "This settings comparison makes the responsive redesign immediately visible.",
"captures": [
{
"id": "responsive-settings",
@ -79,6 +80,7 @@ class ReleaseNoteVisualPlanTest(unittest.TestCase):
def test_structured_output_schema_carries_public_and_capture_bounds(self):
schema = visuals.json_schema()
self.assertEqual(schema["properties"]["schema_version"]["type"], "integer")
self.assertIn("decision", schema["required"])
captures = schema["properties"]["captures"]
capture = captures["items"]["properties"]
self.assertEqual(captures["maxItems"], visuals.MAX_CAPTURES)
@ -88,6 +90,18 @@ class ReleaseNoteVisualPlanTest(unittest.TestCase):
visuals.MAX_STEPS,
)
def test_empty_capture_plan_requires_an_explicit_model_decision(self):
with self.assertRaisesRegex(visuals.PlanError, "decision"):
visuals.validate_plan({"schema_version": 1, "captures": []})
plan = visuals.validate_plan(
{
"schema_version": 1,
"decision": "The investigated changes have no meaningful static visual state.",
"captures": [],
}
)
self.assertEqual(plan["captures"], [])
def test_current_only_capture_has_one_asset(self):
raw = valid_plan()
raw["captures"][0]["before"] = None