fix: patch checks full target span + marker injection/integrity (algo #8)
This commit is contained in:
@@ -309,6 +309,36 @@ class TestValidateSkill:
|
||||
result = validate_skill(orig, "no frontmatter body")
|
||||
assert not result.passed
|
||||
|
||||
def test_unpaired_appendix_marker_fails(self) -> None:
|
||||
"""evolved 出现孤立 APPENDIX_START(无配对 END)→ marker 完整性校验失败。"""
|
||||
orig = "---\nname: a\ndescription: d\ntask_type: t\n---\nbody body body"
|
||||
evol = f"---\nname: a\ndescription: d\ntask_type: t\n---\nbody {APPENDIX_START} body"
|
||||
result = validate_skill(orig, evol)
|
||||
assert not result.passed
|
||||
assert any("marker" in e.lower() or "配对" in e for e in result.errors)
|
||||
|
||||
def test_duplicate_momentum_marker_fails(self) -> None:
|
||||
"""evolved 出现两对 MOMENTUM marker → 完整性校验失败(各至多一对)。"""
|
||||
orig = "---\nname: a\ndescription: d\ntask_type: t\n---\nbody body body"
|
||||
evol = (
|
||||
"---\nname: a\ndescription: d\ntask_type: t\n---\n"
|
||||
f"{MOMENTUM_START}x{MOMENTUM_END} mid {MOMENTUM_START}y{MOMENTUM_END}"
|
||||
)
|
||||
result = validate_skill(orig, evol)
|
||||
assert not result.passed
|
||||
assert any("marker" in e.lower() or "配对" in e for e in result.errors)
|
||||
|
||||
def test_paired_markers_pass(self) -> None:
|
||||
"""evolved 含成对 appendix+momentum marker(各一对,顺序正确)→ 校验通过。"""
|
||||
body = "body " * 20
|
||||
orig = f"---\nname: a\ndescription: d\ntask_type: t\n---\n{body}"
|
||||
evol = (
|
||||
f"---\nname: a\ndescription: d\ntask_type: t\n---\n{body}"
|
||||
f"{APPENDIX_START}\n- n\n{APPENDIX_END}\n{MOMENTUM_START}\nm\n{MOMENTUM_END}"
|
||||
)
|
||||
result = validate_skill(orig, evol)
|
||||
assert result.passed, result.errors
|
||||
|
||||
|
||||
class TestValidateSystem:
|
||||
"""validate_system 测试。"""
|
||||
|
||||
@@ -383,3 +383,68 @@ class TestApplyPatch:
|
||||
assert "line2" not in out
|
||||
assert "TAIL" in out
|
||||
assert all(r["status"].startswith("applied") for r in reports)
|
||||
|
||||
|
||||
class TestSpanOverlapProtection:
|
||||
"""冻结区跨度保护 + marker 注入拦截(算法 #8 加固)。"""
|
||||
|
||||
def test_replace_spanning_into_protected_marker_preserved(self) -> None:
|
||||
"""target 末端跨入含 marker 的 appendix 冻结区时被拦截,marker 不被破坏。"""
|
||||
body = "正文最后一段。"
|
||||
appendix = f"{APPENDIX_START}\n## 执行提醒\n- 规则A\n{APPENDIX_END}"
|
||||
content = body + "\n\n" + appendix
|
||||
# target 从正文末尾跨入 APPENDIX_START(含 marker 字面量)
|
||||
target = "正文最后一段。\n\n" + APPENDIX_START
|
||||
edits = [{"op": "delete", "target": target, "content": ""}]
|
||||
new_content, report = apply_patch_with_report(
|
||||
content, edits, protected_spans=[appendix]
|
||||
)
|
||||
# 无论经 marker 注入拦截还是跨度拦截,marker 都必须完整保留
|
||||
assert APPENDIX_START in new_content and APPENDIX_END in new_content
|
||||
assert report[0]["status"] in ("skipped_protected", "skipped_marker_injection")
|
||||
|
||||
def test_replace_spanning_into_frozen_section_is_skipped(self) -> None:
|
||||
"""target 起点在正文、末端伸入无 marker 的冻结区段 → 跨度拦截跳过。"""
|
||||
body = "可改正文段落。"
|
||||
frozen = "## 输出格式\n必须输出 JSON。"
|
||||
content = body + "\n" + frozen
|
||||
# target 从可改正文跨入冻结区段(不含任何 marker 字面量)
|
||||
target = "可改正文段落。\n## 输出格式"
|
||||
edits = [{"op": "replace", "target": target, "content": "破坏内容"}]
|
||||
new_content, report = apply_patch_with_report(
|
||||
content, edits, protected_spans=[frozen]
|
||||
)
|
||||
assert "破坏内容" not in new_content
|
||||
assert "## 输出格式" in new_content
|
||||
assert report[0]["status"] == "skipped_protected"
|
||||
|
||||
def test_insert_after_spanning_into_frozen_section_is_skipped(self) -> None:
|
||||
"""insert_after 的 target 末端伸入无 marker 冻结区段时跨度拦截跳过。"""
|
||||
body = "可改正文。"
|
||||
frozen = "## 输出格式\n固定内容。"
|
||||
content = body + "\n" + frozen
|
||||
target = "可改正文。\n## 输出格式"
|
||||
edits = [{"op": "insert_after", "target": target, "content": "注入内容"}]
|
||||
new_content, report = apply_patch_with_report(
|
||||
content, edits, protected_spans=[frozen]
|
||||
)
|
||||
assert "注入内容" not in new_content
|
||||
assert report[0]["status"] == "skipped_protected"
|
||||
|
||||
def test_edit_payload_with_marker_literal_rejected(self) -> None:
|
||||
"""edit payload 含 marker 字面量 → 拒绝该 edit。"""
|
||||
edits = [
|
||||
{"op": "append", "target": "", "content": f"注入 {APPENDIX_START} 破坏"}
|
||||
]
|
||||
_, report = apply_patch_with_report("正文", edits, protected_spans=[])
|
||||
assert any(
|
||||
"marker" in str(s).lower() or "reject" in str(s).lower() for s in report[0].values()
|
||||
)
|
||||
|
||||
def test_edit_target_with_marker_literal_rejected(self) -> None:
|
||||
"""edit target 含 momentum marker 字面量 → 拒绝该 edit。"""
|
||||
edits = [
|
||||
{"op": "replace", "target": f"{MOMENTUM_START}x", "content": "y"}
|
||||
]
|
||||
_, report = apply_patch_with_report("正文", edits, protected_spans=[])
|
||||
assert report[0]["status"] == "skipped_marker_injection"
|
||||
|
||||
Reference in New Issue
Block a user