This commit is contained in:
Santh 2026-08-12 22:22:03 +07:00 committed by GitHub
commit a7a76460ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 50 additions and 4 deletions

1
.gitignore vendored
View file

@ -51,6 +51,7 @@ tests/skill_engine/*
!tests/skill_engine/test_evolver_length_recovery.py
!tests/skill_engine/test_evolution_retry_idempotency.py
!tests/skill_engine/test_analyzer_length_recovery.py
!tests/skill_engine/test_detect_patch_type_prose.py
!tests/skill_engine/decision/
tests/skill_engine/decision/*
!tests/skill_engine/decision/test_analysis_adapter.py

View file

@ -482,11 +482,14 @@ def detect_patch_type(content: str) -> PatchType:
if "*** Begin Files" in content:
return PatchType.FULL
# Detect bare *** File: markers (no *** Begin Files envelope).
# Must appear at line start; require at least one to look structural.
file_header_hits = _FILE_HEADER_RE.findall(content)
if file_header_hits:
# ≥2 File: markers, or one at content start with a body (docstring rule 3).
file_headers = list(_FILE_HEADER_RE.finditer(content))
if len(file_headers) >= 2:
return PatchType.FULL
if len(file_headers) == 1:
match = file_headers[0]
if not content[: match.start()].strip() and content[match.end() :].strip():
return PatchType.FULL
if "<<<<<<< SEARCH" in content:
return PatchType.DIFF
@ -527,6 +530,10 @@ def parse_multi_file_full(content: str) -> Dict[str, str]:
# No multi-file markers — treat entire content as SKILL.md
return {SKILL_FILENAME: content}
# Single mid-document *** File: is prose/example text, not multi-file.
if len(headers) == 1 and stripped[: headers[0].start()].strip():
return {SKILL_FILENAME: content}
files: Dict[str, str] = {}
for i, match in enumerate(headers):
file_path = match.group(1).strip()

View file

@ -0,0 +1,38 @@
"""Regression: mid-document *** File: prose must not drop SKILL.md."""
from __future__ import annotations
from pathlib import Path
from openspace.skill_engine.patch import (
PatchType,
create_skill,
detect_patch_type,
parse_multi_file_full,
)
def test_detect_patch_type_ignores_single_mid_document_file_marker():
content = "# My Skill\n\n*** File: helper.sh\n#!/bin/bash\necho hi\n"
assert detect_patch_type(content) == PatchType.FULL
parsed = parse_multi_file_full(content)
assert list(parsed.keys()) == ["SKILL.md"]
assert "*** File: helper.sh" in parsed["SKILL.md"]
def test_create_skill_keeps_skill_md_when_file_marker_is_prose(tmp_path: Path):
content = "# My Skill\n\n*** File: helper.sh\n#!/bin/bash\necho hi\n"
result = create_skill(tmp_path / "s", content)
assert result.ok
skill_md = tmp_path / "s" / "SKILL.md"
assert skill_md.is_file()
assert "*** File: helper.sh" in skill_md.read_text()
assert not (tmp_path / "s" / "helper.sh").exists()
def test_detect_patch_type_accepts_structural_multi_file_at_start():
content = "*** File: SKILL.md\n# Skill\n\n*** File: helper.sh\n#!/bin/bash\necho hi\n"
assert detect_patch_type(content) == PatchType.FULL
parsed = parse_multi_file_full(content)
assert "SKILL.md" in parsed
assert "helper.sh" in parsed