@@ -291,3 +291,95 @@ async def test_parse_valid_file_still_works(tmp_path):
291291 assert result .frontmatter .title == "Valid File"
292292 assert result .frontmatter .type == "knowledge"
293293 assert result .frontmatter .tags == ["test" , "valid" ]
294+
295+
296+ @pytest .mark .asyncio
297+ async def test_invalid_yaml_does_not_add_metadata_key (tmp_path ):
298+ """Test that invalid YAML doesn't create spurious 'metadata' key in frontmatter (issue #528).
299+
300+ This tests a bug where `frontmatter.Post(content, metadata={})` was used incorrectly.
301+ The `metadata={}` kwarg creates a KEY called "metadata" in the metadata dict,
302+ rather than setting the metadata to an empty dict.
303+
304+ This caused files with invalid YAML to get `metadata: {}` in their frontmatter output,
305+ which is incorrect and confusing.
306+ """
307+ # Create a file with completely broken YAML that will trigger the fallback path
308+ test_file = tmp_path / "broken_yaml.md"
309+ content = dedent (
310+ """
311+ ---
312+ title: Invalid YAML
313+ this is: [not, valid, yaml
314+ missing: closing bracket
315+ ---
316+ # Content
317+
318+ This file has broken YAML frontmatter.
319+ """
320+ ).strip ()
321+ test_file .write_text (content )
322+
323+ # Parse the file
324+ parser = EntityParser (tmp_path )
325+ result = await parser .parse_file (test_file )
326+
327+ # The metadata dict should NOT contain a "metadata" key
328+ # This was the bug: frontmatter.Post(content, metadata={}) creates {"metadata": {}}
329+ assert "metadata" not in result .frontmatter .metadata , (
330+ "Frontmatter metadata should not contain a 'metadata' key. "
331+ "This indicates the bug where Post(content, metadata={}) was used incorrectly."
332+ )
333+
334+ # Should still have the expected defaults
335+ assert result .frontmatter .title == "broken_yaml"
336+ assert result .frontmatter .type == "note"
337+
338+
339+ @pytest .mark .asyncio
340+ async def test_frontmatter_roundtrip_preserves_user_metadata (tmp_path ):
341+ """Test that parsing and re-serializing frontmatter preserves user fields (issue #528).
342+
343+ Users reported that after cloud sync, their custom frontmatter fields like 'citekey'
344+ were being lost and replaced with defaults. This test ensures user metadata is preserved.
345+ """
346+ from basic_memory .file_utils import dump_frontmatter
347+ import frontmatter
348+
349+ # Create a file with user's custom frontmatter (like the bug report)
350+ test_file = tmp_path / "litnote.md"
351+ content = dedent (
352+ '''
353+ ---
354+ title: "My Document Title"
355+ type: litnote
356+ tags:
357+ - research
358+ - methodology
359+ citekey: authorTitleYear2024
360+ ---
361+
362+ # Content here...
363+ '''
364+ ).strip ()
365+ test_file .write_text (content )
366+
367+ # Parse the file
368+ parser = EntityParser (tmp_path )
369+ result = await parser .parse_file (test_file )
370+
371+ # User's custom fields should be preserved
372+ assert result .frontmatter .title == "My Document Title"
373+ assert result .frontmatter .type == "litnote" # NOT overwritten to "note"
374+ assert "citekey" in result .frontmatter .metadata
375+ assert result .frontmatter .metadata ["citekey" ] == "authorTitleYear2024"
376+
377+ # Simulate what write_frontmatter does
378+ post = frontmatter .Post (result .content , ** result .frontmatter .metadata )
379+ output = dump_frontmatter (post )
380+
381+ # The output should NOT have duplicate frontmatter or metadata: {} key
382+ assert output .count ("---" ) == 2 , "Should have exactly one frontmatter block (two --- delimiters)"
383+ assert "metadata:" not in output , "Should not have 'metadata:' key in output"
384+ assert "citekey: authorTitleYear2024" in output , "User's citekey should be preserved"
385+ assert "type: litnote" in output , "User's type should be preserved"
0 commit comments