You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Maximum merging can wipe out entire list properties: two merge groups write the same key and the second overwrites the first #176
MergeSimilarProperties (Frontmatter/PropertyMerger.cs:40-52) writes each canonical-name group into mergedFrontmatter without checking whether another group has already written that key. This goes wrong under Maximum:
A random fuzz over 2–6 keys, all with list values, found this loss only under Maximum. Two of the inputs it hit:
[keywords, author_name, author, name_of_author]
[desc, writer, name_of_author, authors, description, author], which loses writer, name_of_author and authors
Why it matters
List merging is meant to be lossless: items are appended, and repeats are kept per #142. Here whole lists disappear with no warning.
#167 reports the order-dependent mapping, which is the root cause. Fixing its scoring alone would still leave MergeSimilarProperties with no guard against two groups writing the same key. This issue asks for that guard.
Suggested fix / acceptance criteria
Make the writes in MergeSimilarProperties collision-safe. When the target key is already present, either merge into it (append list items, apply the scalar rule) or keep the colliding keys apart.
Add a test invariant: for every strategy, the multiset of list items in the input equals the multiset in the output.
Acceptance:
The repro keeps a1, b1 and c1.
A permutation or fuzz test over key orders under Maximum loses no list item.
Related: #167 (root cause) and PR #173. The PR changes the lone-list canonicalKey rule but doesn't fix this, because here canonicalKey already equals the original key.
Area / suggested assignment:Frontmatter/PropertyMerger.cs: MergeSimilarProperties (20-56). The grouping loop is at 42-53 and the canonicalKey fallback at 46-50. MergePropertyGroup (117-164) writes unguarded at 162 (target[originalKeys[0]], scalar path), and MergeArrayValues (166-189) at 188 (target[canonicalKey]). The mixed-type branch in MergePropertyGroup also writes target[key] without a check.
Notes: Route every write into mergedFrontmatter through one helper, for example WriteMerged(target, key, value). If the key is already present and both values are lists, append them using the same "drop only items an earlier list supplied" rule. If they are scalars or of mixed types, keep both under their original keys rather than overwriting. Doing both is simpler than reasoning about which group "owns" a key. Tests: the repro keeps a1, b1 and c1. Add a permutation test over the orderings of both fuzz inputs from the issue. Add the invariant check (the multiset of input list items equals the multiset of output list items) for every FrontmatterMergeStrategy.
What's wrong
MergeSimilarProperties(Frontmatter/PropertyMerger.cs:40-52) writes each canonical-name group intomergedFrontmatterwithout checking whether another group has already written that key. This goes wrong under Maximum:group.Key = X, while a lone key's group falls back to its own name, alsoX, through thecanonicalKeyrule (:45-49).MergeArrayValuesthen doestarget[canonicalKey] = …(:181) for both groups.Reproduction (HEAD 43a9cdc)
Observed:
a1andc1are gone. It is the same with two items per list:[a1,a2],[b1,b2]and[c1,c2]give justauthor: [b1, b2].Trace, from calling
GetCanonicalNamevia reflection:author_name → authorandname_of_author → author. That makes the group{author_name, name_of_author}, which writestarget["author"] = [a1, c1].author → author_name, because of the word-score tie from Maximum merging depends on key order: name_of_author + author_name merge in one order and stay separate in the other, because each key is scored against itself #167. That makes a lone group{author}. ItscanonicalKeyfalls back to"author", so it writestarget["author"] = [b1]and overwrites the first write.A random fuzz over 2–6 keys, all with list values, found this loss only under Maximum. Two of the inputs it hit:
[keywords, author_name, author, name_of_author][desc, writer, name_of_author, authors, description, author], which loseswriter,name_of_authorandauthorsWhy it matters
List merging is meant to be lossless: items are appended, and repeats are kept per #142. Here whole lists disappear with no warning.
#167 reports the order-dependent mapping, which is the root cause. Fixing its scoring alone would still leave
MergeSimilarPropertieswith no guard against two groups writing the same key. This issue asks for that guard.Suggested fix / acceptance criteria
MergeSimilarPropertiescollision-safe. When the target key is already present, either merge into it (append list items, apply the scalar rule) or keep the colliding keys apart.a1,b1andc1.Related: #167 (root cause) and PR #173. The PR changes the lone-list
canonicalKeyrule but doesn't fix this, because herecanonicalKeyalready equals the original key.