65 lines
1.8 KiB
Python
65 lines
1.8 KiB
Python
"""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()}"
|