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
Aggressive and Maximum merging delete properties whose name merely contains another key: subtitle, tagline, username, author_url, canonical_url and summary_image lose their values #175
PropertyMerger.FindBasicCanonicalName (Frontmatter/PropertyMerger.cs:220-243) pairs two keys whenever one normalized name contains the other (:231-232). It then maps the longer key onto the shorter one through PreferredName.
Aggressive:GetAggressiveCanonicalName (:85-96) accepts the pairing when IsInSameCategory(key, canonicalName) holds. That check passes whenever the target is a known name such as title, tag, name, author or url.
Maximum: accepts the pairing unconditionally (FindSemanticCanonicalName, :261-265).
Once paired, the two keys form one merge group. For scalar values, MergePropertyGroup keeps only the first value (:152-156), so the other property's value is thrown away without any warning.
Each row runs CombineFrontmatter("---\n{k1}: A\n{k2}: B\n---\nBody\n", AsIs, AsIs, strategy) and reads back the resulting keys:
Input keys
Conservative
Aggressive
Maximum
title + subtitle
both kept
title=A only
title=A only
tag + tagline
both kept
tag=A only
tag=A only
name + username
both kept
name=A only
name=A only
author + author_url
both kept
author=A only
author=A only
url + canonical_url
both kept
url=A only
url=A only
summary + summary_image
both kept
summary=A only
summary=A only
image + image_alt, version + version_date, step1 + step10
both kept
both kept
first key only
At scale, a block of 200 distinct scalars property0 … property199 comes back from Maximum with 10 keys. The other 190 values are gone, because property12 "contains" property1, and so on.
Why it matters
subtitle, tagline, username and canonical_url are separate, common fields in Hugo, Jekyll and Obsidian. A stronger merge strategy should combine synonyms. It should not delete fields that happen to share a substring with another field.
This is related to two open issues, but neither covers it:
Only let containment pair two keys when the leftover part is known decoration, meaning the PropertyNameNormalizer prefix and suffix lists. Alternatively, compare whole words instead of raw substrings.
In Aggressive, require both keys to be in the category, not just the target.
Acceptance:
Every pair in the table keeps both values under Aggressive and Maximum.
property0..property199 keeps all 200 keys.
The existing containment and equivalence tests still pass, or are updated deliberately. This includes MergeSimilarProperties_WithContainmentSimilarity_MapsCorrectly and the related/page_related tests.
Priority: High. Values are silently deleted for common, distinct fields (subtitle, tagline, username, canonical_url) under both non-default merge strategies. The property0..199 case shows the loss can be large. Default (Conservative) users are not affected.
Area / suggested assignment:Frontmatter/PropertyMerger.cs: FindBasicCanonicalName (197-250), where the raw-substring containment pairing is at 238-239 and PreferredName at 242. GetAggressiveCanonicalName (85-97) calls IsInSameCategory (99-115), which passes when only the target is known (line 114, ||). FindSemanticCanonicalName (302-336) accepts basicMatch unconditionally at 305-309. The scalar loss happens in MergePropertyGroup (117-164), which keeps only firstValue at 162.
Notes: (1) In FindBasicCanonicalName, pair by containment only when the leftover substring, after removing the shorter name, is a known prefix or suffix from PropertyNameNormalizer, or compare NormalizeToWords sets instead of raw strings. That stops property1/property12 and step1/step10. (2) Change IsInSameCategory to require both key and canonicalName to be in the same mapping, not either one. (3) As a backstop, when a scalar group has differing values, keep them all under their original keys instead of dropping all but the first. Tests: each pair in the table under Aggressive and Maximum, property0..199 keeping 200 keys, and a deliberate review of MergeSimilarProperties_WithContainmentSimilarity_MapsCorrectly (Frontmatter.Test/PropertyMergerTests.cs:353) and EquivalentKeyMergeTests (related/page_related).
Activity
matt-edmondson commentedon Sep 30, 2026
Triage
subtitle,tagline,username,canonical_url) under both non-default merge strategies. Theproperty0..199case shows the loss can be large. Default (Conservative) users are not affected.Frontmatter/PropertyMerger.cs:FindBasicCanonicalName(197-250), where the raw-substring containment pairing is at 238-239 andPreferredNameat 242.GetAggressiveCanonicalName(85-97) callsIsInSameCategory(99-115), which passes when only the target is known (line 114,||).FindSemanticCanonicalName(302-336) acceptsbasicMatchunconditionally at 305-309. The scalar loss happens inMergePropertyGroup(117-164), which keeps onlyfirstValueat 162.author_email→author,previous_version→version#162 and Default CombineFrontmatter renames short keys to longer, unrelated standard fields: id → video, key → keywords, list → unlisted, seo → description_seo #177 use the same containment heuristic inNameStandardizer.FindStandardPropertyMatch, where keys are renamed rather than deleted. A shared "containment only across known decoration" helper inPropertyNameNormalizercould serve all three. Maximum merging can wipe out entire list properties: two merge groups write the same key and the second overwrites the first #176 is another Maximum-only loss path, from group collisions. 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 is the Maximum word-score ordering issue. A key normalizing to a single character is containment-matched against unrelated properties, reattributing and losing values #128 (single-character keys) was the earlier, narrower fix.FindBasicCanonicalName, pair by containment only when the leftover substring, after removing the shorter name, is a known prefix or suffix fromPropertyNameNormalizer, or compareNormalizeToWordssets instead of raw strings. That stopsproperty1/property12andstep1/step10. (2) ChangeIsInSameCategoryto require bothkeyandcanonicalNameto be in the same mapping, not either one. (3) As a backstop, when a scalar group has differing values, keep them all under their original keys instead of dropping all but the first. Tests: each pair in the table under Aggressive and Maximum,property0..199keeping 200 keys, and a deliberate review ofMergeSimilarProperties_WithContainmentSimilarity_MapsCorrectly(Frontmatter.Test/PropertyMergerTests.cs:353) andEquivalentKeyMergeTests(related/page_related).Generated by Claude Code
matt-edmondson commentedon Sep 30, 2026
Covered by #180, which redesigns this bug cluster as a whole. Implement it through that issue rather than individually.
Generated by Claude Code