Skip to content

Commit 367cc19

Browse files
committed
Fix YAML tag serialization in frontmatter
When writing or updating frontmatter with tags, the YAML serialization was causing tags to be malformed with improper line breaks: Before: tags: - '[check-in' - 'qr-code' - 'architecture]' After: tags: [check-in, qr-code, architecture] This fix configures yaml.dump() to use flow style (default_flow_style=None) which ensures lists are properly serialized in a single line format. Fixes issue where Basic Memory's write_note function consistently malforms tags in YAML frontmatter. Signed-off-by: Gilang Gumilar <gilangmlr@gmail.com>
1 parent 39f811f commit 367cc19

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

src/basic_memory/file_utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
218218
new_fm = {**current_fm, **updates}
219219

220220
# Write new file with updated frontmatter
221-
yaml_fm = yaml.dump(new_fm, sort_keys=False, allow_unicode=True)
221+
# Configure YAML to use flow style for lists to prevent tag malformation
222+
yaml_fm = yaml.dump(new_fm, sort_keys=False, allow_unicode=True, default_flow_style=None)
222223
final_content = f"---\n{yaml_fm}---\n\n{content.strip()}"
223224

224225
logger.debug("Updating frontmatter", path=str(path_obj), update_keys=list(updates.keys()))

tests/test_yaml_tag_fix.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Test for YAML tag serialization fix."""
2+
3+
import pytest
4+
from pathlib import Path
5+
import tempfile
6+
from basic_memory.file_utils import update_frontmatter, parse_frontmatter
7+
8+
9+
@pytest.mark.asyncio
10+
async def test_yaml_tags_serialize_correctly():
11+
"""Test that tags are serialized as flow style in YAML frontmatter."""
12+
# Create a temporary file
13+
with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
14+
f.write("---\ntitle: Test Note\n---\n\nTest content")
15+
temp_path = Path(f.name)
16+
17+
try:
18+
# Update with tags
19+
tags = ["check-in", "qr-code", "architecture", "system-design"]
20+
await update_frontmatter(temp_path, {"tags": tags})
21+
22+
# Read back the file
23+
content = temp_path.read_text()
24+
25+
# Parse frontmatter
26+
fm = parse_frontmatter(content)
27+
28+
# Verify tags are preserved correctly
29+
assert fm["tags"] == tags
30+
31+
# Verify the YAML format is correct (flow style)
32+
assert 'tags: [' in content or 'tags: ["' in content
33+
34+
# Ensure it's NOT in the malformed format
35+
assert '- \'["' not in content
36+
assert '- \'"' not in content
37+
38+
finally:
39+
# Clean up
40+
temp_path.unlink()
41+
42+
43+
@pytest.mark.asyncio
44+
async def test_empty_tags_list():
45+
"""Test that empty tags list is handled correctly."""
46+
with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
47+
f.write("---\ntitle: Test Note\n---\n\nTest content")
48+
temp_path = Path(f.name)
49+
50+
try:
51+
await update_frontmatter(temp_path, {"tags": []})
52+
content = temp_path.read_text()
53+
fm = parse_frontmatter(content)
54+
assert fm["tags"] == []
55+
56+
finally:
57+
temp_path.unlink()

0 commit comments

Comments
 (0)