Skip to content

Standard naming lowercases the canonical redirectFrom/redirectTo keys to redirectfrom/redirectto, and StandardOrder cannot place them #150

Description

@matt-edmondson

What's wrong

StandardOrder.PropertyNames holds two camelCase canonical names, redirectFrom and redirectTo (Frontmatter/StandardOrder.cs:184-185). The rest of the library assumes every canonical name is lowercase. The result is that the one input that exactly matches the standard name gets corrupted:

  1. NameStandardizer.StandardizePropertyNames (Frontmatter/NameStandardizer.cs:34-36) sees that the key matches a standard name, ignoring case. It then writes it back as property.Key.ToLowerInvariant() instead of the canonical spelling, so redirectFrom is renamed to redirectfrom.
  2. SortFrontmatterProperties (Frontmatter/Frontmatter.cs:265-270) looks each standard name up case-sensitively. redirectFrom no longer matches the lowercased key, so the property is treated as unknown and goes to the tail, after license, which the standard order puts later.
  3. StandardOrder.Compare (public API, Frontmatter/StandardOrder.cs:220-221) searches the mixed-case array with a.ToLowerInvariant(). It can never find redirectFrom or redirectTo, so it treats them as non-standard names.

Reproduction

Console.WriteLine(Frontmatter.CombineFrontmatter("---\nlicense: MIT\nredirectTo: /new\nzeta: 1\ntitle: Hi\n---\nBody\n"));
Console.WriteLine(StandardOrder.Compare("redirectFrom", "license"));

Actual output (default Standard naming, Sorted, Conservative):

---
title: Hi
license: MIT
redirectto: /new
zeta: 1
---
Body

1

Expected:

---
title: Hi
redirectTo: /new
license: MIT
zeta: 1
---
Body

<negative>

With FrontmatterNaming.AsIs + FrontmatterOrder.Sorted, the same input is ordered correctly (title, redirectTo, license, zeta). So the canonical key is broken only when the caller asks for standardization, and it is the input that already matches the standard name exactly.

Why it matters

Standardization is meant to map variant names onto canonical ones. Here it turns a canonical name into a non-canonical one. Anything that reads the key case-sensitively (static site generators and redirect plugins) no longer sees the redirect, so running CombineFrontmatter with its defaults breaks it without any error. The public StandardOrder.Compare is also wrong for these two names.

Suggested fix / acceptance criteria

  • In NameStandardizer, when a key matches a standard property ignoring case, emit the canonical spelling from StandardOrder.PropertyNames, not ToLowerInvariant(). Either that, or make all canonical names lowercase or snake_case (redirect_from / redirect_to, which Jekyll uses) and keep the list consistent.
  • StandardOrder.Compare finds the index ignoring case, for example Array.FindIndex(PropertyNames, p => string.Equals(p, a, StringComparison.OrdinalIgnoreCase)).
  • Tests:
    • CombineFrontmatter with redirectFrom: /x keeps the key spelled redirectFrom and orders it before license.
    • StandardOrder.Compare("redirectFrom", "license") < 0.
    • A guard test asserting that every entry in PropertyNames round-trips unchanged through StandardizePropertyNames.

Activity

  1. matt-edmondson commented on Sep 27, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: Medium. With the default settings, CombineFrontmatter renames the canonical redirectFrom/redirectTo keys to lowercase without any error, so case-sensitive consumers such as static site generators and redirect plugins stop seeing the redirect. The public StandardOrder.Compare is also wrong for both names.
    • Area / suggested assignee: NameStandardizer.StandardizePropertyNames, StandardOrder.Compare, SortFrontmatterProperties. Owner: @matt-edmondson
    • Duplicates / in progress: not a duplicate. It shares its root cause with FrontmatterOrder.Sorted ignores standard keys that differ only in case (e.g. Title, Date) under AsIs naming, contradicting StandardOrder.Compare #151 (case-sensitive standard-key lookup), so fix both together. No open PR.
    • Next step: emit the canonical spelling from PropertyNames instead of ToLowerInvariant(), make Compare search ignoring case, and add a guard test that every PropertyNames entry round-trips unchanged.

    Generated by Claude Code

  2. matt-edmondson commented on Sep 30, 2026

    @matt-edmondson
    ContributorAuthor

    Covered by #180, which redesigns this bug cluster as a whole. Implement it through that issue rather than individually.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions