What's wrong
NameStandardizer.StandardizePropertyNames (Frontmatter/NameStandardizer.cs, lines 36, 52, 63, 73) writes every renamed property with a plain indexer assignment:
standardizedFrontmatter[name] = property.Value;
There is no check for whether name is already in the dictionary. When two source keys resolve to the same standard name, the later one overwrites the earlier one without any warning.
This is separate from #131 (which is about Aggressive/Maximum merging swapping values). This bug needs no aggressive merging at all:
- Default options (Conservative merge).
PropertyMerger.MergePropertyGroup deliberately keeps both keys when their value types differ. The standardizer then collapses them anyway.
FrontmatterMergeStrategy.None with FrontmatterNaming.Standard. Nothing is merged beforehand, so every alias pair collides.
Reproduction
Run against the current main:
| Input |
Call |
Output |
Lost |
---\nauthor: Alice\ncreator:\n - Bob\n---\nbody\n |
CombineFrontmatter(input) (defaults) |
author:\n- Bob |
author: Alice |
---\nauthor: Alice\ncreator: Bob\n---\nbody\n |
CombineFrontmatter(input, Standard, AsIs, None) |
author: Bob |
Alice |
---\nTitle: A\ntitle: B\n---\nbody\n |
CombineFrontmatter(input, Standard, AsIs, None) |
title: B |
Title: A (line 36 lower-cases Title onto the existing title) |
In the second row the later key wins. PropertyMerger does the opposite and keeps the first, so which value survives depends on which code path runs.
Why it matters
CombineFrontmatter is meant to normalize a document without losing information. With the default options, a document carrying both author and creator loses its author, and the output gives no sign that anything was dropped.
Suggested fix / acceptance criteria
- In
StandardizePropertyNames, only rename a key to its standard name when that name is free, meaning not already in standardizedFrontmatter and not a key that appears later in frontmatter. Otherwise keep the original key unchanged.
- Apply the same guard to the lower-casing branch on line 36.
- The alternative is to send collisions through
PropertyMerger's group-merge logic, so the values are combined the same way the merger combines them.
- Add regression tests for all three rows above. Each must keep both values, under their original keys or merged.
What's wrong
NameStandardizer.StandardizePropertyNames(Frontmatter/NameStandardizer.cs, lines 36, 52, 63, 73) writes every renamed property with a plain indexer assignment:There is no check for whether
nameis already in the dictionary. When two source keys resolve to the same standard name, the later one overwrites the earlier one without any warning.This is separate from #131 (which is about Aggressive/Maximum merging swapping values). This bug needs no aggressive merging at all:
PropertyMerger.MergePropertyGroupdeliberately keeps both keys when their value types differ. The standardizer then collapses them anyway.FrontmatterMergeStrategy.NonewithFrontmatterNaming.Standard. Nothing is merged beforehand, so every alias pair collides.Reproduction
Run against the current
main:---\nauthor: Alice\ncreator:\n - Bob\n---\nbody\nCombineFrontmatter(input)(defaults)author:\n- Bobauthor: Alice---\nauthor: Alice\ncreator: Bob\n---\nbody\nCombineFrontmatter(input, Standard, AsIs, None)author: BobAlice---\nTitle: A\ntitle: B\n---\nbody\nCombineFrontmatter(input, Standard, AsIs, None)title: BTitle: A(line 36 lower-casesTitleonto the existingtitle)In the second row the later key wins.
PropertyMergerdoes the opposite and keeps the first, so which value survives depends on which code path runs.Why it matters
CombineFrontmatteris meant to normalize a document without losing information. With the default options, a document carrying bothauthorandcreatorloses its author, and the output gives no sign that anything was dropped.Suggested fix / acceptance criteria
StandardizePropertyNames, only rename a key to its standard name when that name is free, meaning not already instandardizedFrontmatterand not a key that appears later infrontmatter. Otherwise keep the original key unchanged.PropertyMerger's group-merge logic, so the values are combined the same way the merger combines them.