mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-01 10:42:23 +01:00
fix(skills): preserve version strings across markdown saves
This commit is contained in:
@@ -84,10 +84,14 @@ _FM_KEY_RE = re.compile(r"^([a-z_][a-z0-9_]*):\s*(.*)$", re.IGNORECASE)
|
|||||||
_FM_BLOCK_LIST_RE = re.compile(r"^\s*-\s*(.*)$")
|
_FM_BLOCK_LIST_RE = re.compile(r"^\s*-\s*(.*)$")
|
||||||
|
|
||||||
|
|
||||||
def _parse_scalar(raw: str) -> Any:
|
def _parse_scalar(raw: str, *, string_only: bool = False) -> Any:
|
||||||
raw = raw.strip()
|
raw = raw.strip()
|
||||||
if raw == "":
|
if raw == "":
|
||||||
return ""
|
return ""
|
||||||
|
# String fields keep their spelling; quoted values still use the decoder
|
||||||
|
# below so existing escapes round-trip as before.
|
||||||
|
if string_only and not (raw[0] == raw[-1] and raw[0] in ("'", '"')):
|
||||||
|
return raw
|
||||||
if raw.startswith("[") and raw.endswith("]"):
|
if raw.startswith("[") and raw.endswith("]"):
|
||||||
inner = raw[1:-1].strip()
|
inner = raw[1:-1].strip()
|
||||||
if not inner:
|
if not inner:
|
||||||
@@ -171,7 +175,7 @@ def parse_frontmatter(text: str) -> tuple[Dict[str, Any], str]:
|
|||||||
pending_key = key
|
pending_key = key
|
||||||
fm[key] = []
|
fm[key] = []
|
||||||
else:
|
else:
|
||||||
fm[key] = _parse_scalar(val)
|
fm[key] = _parse_scalar(val, string_only=(key == "version"))
|
||||||
pending_key = None
|
pending_key = None
|
||||||
continue
|
continue
|
||||||
m2 = _FM_BLOCK_LIST_RE.match(line)
|
m2 = _FM_BLOCK_LIST_RE.match(line)
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"""Issue #2131: editing a skill must not reinterpret its version as a number."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from services.memory.skill_format import Skill, parse_frontmatter
|
||||||
|
from services.memory.skills import SkillsManager
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"raw, expected",
|
||||||
|
[
|
||||||
|
("1.10", "1.10"),
|
||||||
|
("2.20", "2.20"),
|
||||||
|
("01", "01"),
|
||||||
|
("0", "0"),
|
||||||
|
("1.0.0", "1.0.0"),
|
||||||
|
("true", "true"),
|
||||||
|
("null", "null"),
|
||||||
|
('"1.10"', "1.10"),
|
||||||
|
("'2.20'", "2.20"),
|
||||||
|
('"release\\u002d01"', "release-01"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_version_survives_repeated_markdown_saves(raw, expected):
|
||||||
|
markdown = f"---\nname: demo\nversion: {raw}\nconfidence: 0.80\n---\n"
|
||||||
|
for _ in range(3):
|
||||||
|
skill = Skill.from_markdown(markdown)
|
||||||
|
assert skill.version == expected
|
||||||
|
assert skill.confidence == 0.8
|
||||||
|
markdown = skill.to_markdown()
|
||||||
|
|
||||||
|
|
||||||
|
def test_version_preservation_keeps_other_frontmatter_types():
|
||||||
|
fm, _ = parse_frontmatter(
|
||||||
|
"---\nversion: 1.10\nconfidence: 0.80\nuses: 3\n"
|
||||||
|
"enabled: true\ntags: [one, two]\n---\n"
|
||||||
|
)
|
||||||
|
assert fm == {
|
||||||
|
"version": "1.10", "confidence": 0.8, "uses": 3,
|
||||||
|
"enabled": True, "tags": ["one", "two"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version_line", ["", "version:\n", 'version: ""\n'])
|
||||||
|
def test_missing_or_empty_version_retains_default(version_line):
|
||||||
|
skill = Skill.from_markdown(f"---\nname: demo\n{version_line}---\n")
|
||||||
|
assert skill.version == "1.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_version_survives_unrelated_edits_on_disk(tmp_path):
|
||||||
|
manager = SkillsManager(str(tmp_path))
|
||||||
|
manager.add_skill(name="demo", version="1.10", owner="alice")
|
||||||
|
for i in range(3):
|
||||||
|
assert manager.update_skill("demo", {"description": f"Edit {i}"}, owner="alice")
|
||||||
|
stored = manager.load(owner="alice")
|
||||||
|
assert stored[0]["version"] == "1.10"
|
||||||
|
assert stored[0]["description"] == f"Edit {i}"
|
||||||
|
markdown = next((tmp_path / "skills").rglob("SKILL.md")).read_text(encoding="utf-8")
|
||||||
|
assert "version: 1.10\n" in markdown
|
||||||
Reference in New Issue
Block a user