Files
cvfs/dlib/cv/ats_guard.py
2026-04-02 19:15:47 +02:00

45 lines
1.5 KiB
Python

from __future__ import annotations
from typing import Iterable
from .schema import PatchPayload, PatchOperation, StructuredDocument
class PatchValidationError(ValueError):
pass
def validate_patchset(
document: StructuredDocument,
patches: Iterable[PatchPayload],
*,
max_changes: int = 12,
max_growth_ratio: float = 1.45,
) -> None:
patch_list = list(patches)
if len(patch_list) > max_changes:
raise PatchValidationError(
f"Patchset exceeds max changes ({len(patch_list)} > {max_changes})"
)
block_map = {block.path: block for block in document.blocks}
for patch in patch_list:
block = block_map.get(patch.target_path)
if not block:
raise PatchValidationError(
f"Target path {patch.target_path} does not exist in base document"
)
if patch.operation == PatchOperation.REPLACE_TEXT:
if not patch.new_value:
raise PatchValidationError("replace_text requires new_value")
baseline = len(block.text.strip()) or 1
if len(patch.new_value.strip()) / baseline > max_growth_ratio:
raise PatchValidationError("Patch grows text beyond ATS safe threshold")
if (
patch.operation
in {PatchOperation.REMOVE_BLOCK, PatchOperation.SUPPRESS_BLOCK}
and block.block_type == "heading"
):
raise PatchValidationError(
"Headings cannot be removed without manual confirmation"
)