"""Canonical reviewed Bug task payload shared by intake and validation.""" from __future__ import annotations import hashlib import json import re from typing import Any LEGACY_PAYLOAD_FIELDS = ( "title", "description", "priority", "actual", "expected", "stepsToReproduce", "fixLogic", "acceptanceCriteria", ) CLARIFIED_PAYLOAD_FIELDS = ( "title", "description", "actual", "expected", "acceptanceCriteria", ) NUMBERED_ITEM = re.compile(r"(?:^|\s)([1-9][0-9]*)\.\s+") def review_items(value: str) -> list[str]: """Recover line or numbered-list review text as stable non-empty items.""" lines = [line.strip(" \t-*•") for line in value.splitlines() if line.strip()] if len(lines) > 1: return lines text = value.strip() matches = list(NUMBERED_ITEM.finditer(text)) if matches: items: list[str] = [] for index, match in enumerate(matches): start = match.end() end = matches[index + 1].start() if index + 1 < len(matches) else len(text) item = text[start:end].strip() if item: items.append(item) if items: return items return [text] if text else [] def approval_payload_hash(task: dict[str, Any]) -> str: """Hash the exact reviewed fields that Developer and Test consume.""" source = task.get("source") workflow = source.get("workflow") if isinstance(source, dict) else None fields = ( CLARIFIED_PAYLOAD_FIELDS if workflow == "clarified-writeback-v1" else LEGACY_PAYLOAD_FIELDS ) payload = {field: task.get(field) for field in fields} encoded = json.dumps( payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"), ).encode("utf-8") return f"sha256:{hashlib.sha256(encoded).hexdigest()}"