54 lines
1.6 KiB
Python
54 lines
1.6 KiB
Python
"""Preservation checks for skill revision drafts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
|
|
def check_preservation(*, base_content: str, draft_content: str) -> dict[str, Any]:
|
|
base_sections = _sections(base_content)
|
|
draft_sections = _sections(draft_content)
|
|
preserved: list[str] = []
|
|
changed: list[str] = []
|
|
dropped: list[str] = []
|
|
|
|
for heading, body in base_sections.items():
|
|
draft_body = draft_sections.get(heading)
|
|
if draft_body is None:
|
|
dropped.append(heading)
|
|
continue
|
|
preserved.append(heading)
|
|
if _normalize(body) != _normalize(draft_body):
|
|
changed.append(heading)
|
|
|
|
risk_level = "high" if dropped else "low"
|
|
return {
|
|
"passed": not dropped,
|
|
"risk_level": risk_level,
|
|
"preserved_sections": preserved,
|
|
"changed_sections": changed,
|
|
"dropped_sections": dropped,
|
|
}
|
|
|
|
|
|
def _sections(content: str) -> dict[str, str]:
|
|
current = "body"
|
|
sections: dict[str, list[str]] = {current: []}
|
|
for line in (content or "").splitlines():
|
|
match = re.match(r"^#{1,6}\s+(.+?)\s*$", line)
|
|
if match:
|
|
current = match.group(1).strip()
|
|
sections.setdefault(current, [])
|
|
continue
|
|
sections.setdefault(current, []).append(line)
|
|
return {
|
|
heading: "\n".join(lines).strip()
|
|
for heading, lines in sections.items()
|
|
if "\n".join(lines).strip()
|
|
}
|
|
|
|
|
|
def _normalize(value: str) -> str:
|
|
return re.sub(r"\s+", " ", value or "").strip().lower()
|