fix: patch checks full target span + marker injection/integrity (algo #8)

This commit is contained in:
2026-07-16 06:20:29 -04:00
parent caea9150b3
commit 65126feada
4 changed files with 178 additions and 7 deletions
+40
View File
@@ -19,6 +19,8 @@ from loguru import logger
from core.evolution.patch import (
APPENDIX_END,
APPENDIX_START,
MOMENTUM_END,
MOMENTUM_START,
append_to_appendix,
apply_patch_with_report,
extract_appendix_notes,
@@ -293,10 +295,39 @@ def _tool_protected_spans(text: str) -> list[str]:
# =========================================================================
def _check_marker_integrity(evolved: str) -> list[str]:
"""校验 evolved 中冻结区 marker 的完整性(成对、至多一对、START 先于 END)。
进化写入可能破坏 appendix/momentum marker 配对,破坏后 append_to_appendix /
replace_momentum 等下游会静默误拼或抛错。此处集中拦截:任一 marker 对违反
「START 数==END 数、各至多一对、START 在 END 前」即整体 reject。
参数:
evolved: 改写后的全文。
返回:
错误信息列表(空列表表示 marker 完整)。
"""
errors: list[str] = []
for name, start_m, end_m in (
("APPENDIX", APPENDIX_START, APPENDIX_END),
("MOMENTUM", MOMENTUM_START, MOMENTUM_END),
):
s = evolved.count(start_m)
e = evolved.count(end_m)
if s != e:
errors.append(f"{name} marker 不配对:START={s} END={e}")
elif s > 1:
errors.append(f"{name} marker 出现多对({s}),至多一对")
elif s == 1 and evolved.index(start_m) > evolved.index(end_m):
errors.append(f"{name} marker 顺序错误:END 出现在 START 之前")
return errors
def validate_skill(original: str, evolved: str) -> ValidationResult:
"""校验 Skill 改写结果。
检查项: frontmatter 三字段保留(name / description / task_type)、
marker 完整性(appendix/momentum 成对且至多一对、顺序正确)、
长度比在 [0.3, 2.0]、代码块闭合。
参数:
@@ -305,6 +336,11 @@ def validate_skill(original: str, evolved: str) -> ValidationResult:
返回:
ValidationResult 实例。
关键实现细节:
marker 完整性先于长度校验——长度校验经 _strip_protected_regions 调用
momentum_region_bounds,对损坏 marker 会抛 ValueError;故 marker 破坏时先
返回失败,避免异常穿透且明确 reject 该候选。
"""
errors: list[str] = []
orig_fm = _parse_frontmatter(original)
@@ -319,6 +355,10 @@ def validate_skill(original: str, evolved: str) -> ValidationResult:
errors.append(
f"frontmatter 字段 {key} 被修改: {orig_fm.get(key)!r}{evol_fm.get(key)!r}"
)
marker_errors = _check_marker_integrity(evolved)
if marker_errors:
errors.extend(marker_errors)
return ValidationResult(passed=False, errors=errors)
errors.extend(_check_length(original, evolved))
errors.extend(_check_code_blocks(evolved))
return ValidationResult(passed=len(errors) == 0, errors=errors)