Skip to content

chore: The CSharpier gate cannot pass on a non-Windows checkout (CRLF vs LF) #129

Description

@wallstop

Problem

.llm/context.md's validation ladder requires dotnet tool run csharpier -- check Editor Runtime Tests to be green before review. On a clean Linux or macOS checkout that command can never pass: it reports every tracked C# file as unformatted.

Reproduction (repository at 9bbd7f2, verified 2026-10-02)

git clone https://github.com/wallstop/DataVisualizer.git && cd DataVisualizer
dotnet tool restore
dotnet tool run csharpier -- check Editor Runtime Tests

Actual: Checked 110 files and 110 Error ... Was not formatted. The file contained different line endings than formatting it would result in. lines, exit code 2. CSharpier 1.1.2, as pinned in .config/dotnet-tools.json.

Evidence that no source file is misformatted:

  • All 112 tracked *.cs blobs in git contain zero CRLF and zero lone CR bytes, so no file has mixed endings.
  • dotnet tool run csharpier -- format <one file> changes nothing but line endings: the diff is every line, and the result has CRLF on all lines. Normalizing both sides to LF makes the two files byte-identical.
  • .editorconfig line 4 sets end_of_line = crlf for [*], so CSharpier writes CRLF.
  • .gitattributes line 4 sets * text=auto, so git stores LF in the repository and checks out LF on Linux and macOS (native) but CRLF on Windows. The gate therefore passes only on Windows.

Expected

csharpier -- check is green on every supported contributor platform, or the configuration states one canonical line ending that git and CSharpier agree on everywhere.

Impact

  • Contributors on Linux and macOS cannot run the documented gate, and the .pre-commit-config.yaml CSharpier hook rewrites every staged C# file to CRLF in the working tree while git normalizes it straight back to LF, so the change never lands and the churn repeats.
  • CI does not catch it: .github/workflows/llm-lint.yml runs the .llm linters and the harness self-tests but never CSharpier.

Acceptance criteria

  1. dotnet tool run csharpier -- check Editor Runtime Tests exits 0 on Linux, macOS, and Windows from a fresh clone.
  2. One canonical line ending, with .gitattributes and .editorconfig agreeing: either git checks out CRLF for *.cs on every platform, or .editorconfig stops requiring CRLF.
  3. The pre-commit CSharpier hook leaves a committed file byte-identical after a no-op format run on a non-Windows host.
  4. A check that fails when the two disagree, so the drift cannot return silently.

Notes

Found while validating a documentation-only change that touched no C# file, so the failure is pre-existing on main and unrelated to that work.

Activity

  1. added
    triageNeeds maintainer classification and follow-up
    on Oct 2, 2026
  2. wallstop commented on Oct 2, 2026

    @wallstop
    OwnerAuthor

    Found while validating PR #130, which touches no C# file, so the failure is pre-existing on main.

    The reproduction in this issue is the one that matters: on a fresh Linux clone, dotnet tool run csharpier -- check Editor Runtime Tests reports all 110 checked files as Was not formatted. The file contained different line endings than formatting it would result in., exit code 2.

    Additional evidence gathered while confirming it:

    • Formatting any single .cs file changes nothing but line endings. The unified diff shows every line changed, the formatted result has CRLF on all lines, and normalizing both sides to LF makes the two files byte-identical.
    • git status after such a format reports CRLF will be replaced by LF the next time Git touches it, which is why the churn never lands as a commit.
    • The cause is the pair .editorconfig ([*] end_of_line = crlf) and .gitattributes (* text=auto). Git stores LF and checks out LF on Linux and macOS, so CSharpier disagrees with the working tree everywhere except Windows.
  3. wallstop commented on Oct 2, 2026

    @wallstop
    OwnerAuthor

    Fixed by PR #131, verified on a Linux checkout of this repository.

    • Red: dotnet tool run csharpier -- check Editor Runtime Tests reported all 110 checked files as Was not formatted. The file contained different line endings than formatting it would result in., exit 1.
    • Root cause confirmed: .editorconfig [ * ] required end_of_line = crlf, .gitattributes set * text=auto with no eol, and all 112 tracked *.cs blobs are LF-only.
    • Green: .gitattributes now pins * text=auto eol=lf and .editorconfig declares end_of_line = lf. The same command reports Checked 110 files and exits 0. A no-op format of a tracked file leaves its blob hash unchanged, and a core.autocrlf=true checkout simulation produces LF C# files, which is the Windows default that used to pass by accident.
    • Criterion 4: scripts/lint-line-endings.js resolves every path either file names on both sides and fails closed when the endings differ, when a text rule omits eol, when eol is set without text, when a binary rule pins an ending, when the base * rule or the [*] section is missing, or when an ending name is unknown. It runs in lint:llm:full, the llm-lint workflow on both operating systems, and pre-commit. 25 self-test cases cover it, and five seeded regressions are each killed by a distinct case.

    Acceptance criteria 1 to 4 are met. Criterion 4 does not add CSharpier to CI; the harness stays tool-light and the config pair is guarded instead.

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

    triageNeeds maintainer classification and follow-up

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions