Skip to content

Default to LF line endings, and add a local SonarCloud reproduction - #90

Merged
matt-edmondson merged 2 commits into
mainfrom
claude/extract-generalize-codegen-hjt2iy
Aug 27, 2026
Merged

Default to LF line endings, and add a local SonarCloud reproduction#90
matt-edmondson merged 2 commits into
mainfrom
claude/extract-generalize-codegen-hjt2iy

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Closes #89. Addresses #88 — with one acceptance criterion I could not meet; see below.

#89 — LF becomes the default line terminator [major]

CodeBlocker writes through IndentedTextWriter, which terminates lines with Environment.NewLine. That made the same calls produce different bytes on different machines. Generated code is almost always committed, diffed, or compared against a golden file, and every one of those wants reproducibility more than it wants the local convention — so the deterministic terminator is the better default, and the old one is the thing worth opting into.

  • CodeBlocker.DefaultNewLineString is NewLines.Lf, used by every constructor and factory overload that does not take a terminator, and substituted for a null one.
  • NewLines.Host is unchanged and still reachable: CodeBlocker.Create(CodeBlocker.DefaultIndentString, NewLines.Host).
  • Tagged [major]: anyone on Windows relying on the old default gets different bytes out.

The test suite was asserting the bug into place. Every expectation was written against Environment.NewLine, which on a Linux runner is LF — so the suite would have gone green on this change while Windows silently broke. All of them now assert against CodeBlocker.DefaultNewLineString, and NewLineTests gained explicit coverage that the default is LF regardless of host and that the host terminator is still available by asking for it.

README.md is reworded to match: NewLines.Host is presented as the opt-in rather than the default, the samples drop the now-redundant explicit NewLines.Lf argument, and the API reference gains a Constants table.

#88 — local SonarCloud reproduction [patch]

CI runs the Sonar analyzers via the SonarCloud scanner; a plain dotnet build does not, so findings only surface after a push and the bot comment links to a dashboard rather than naming them. .sonarlint/ wires the same analyzers into a local build, imported by nothing and pointed at explicitly:

dotnet build -p:CustomAfterMicrosoftCommonProps=$PWD/.sonarlint/sonar-local.props

After, not Before. Every project here declares its SDK with <Sdk Name="..." /> elements rather than the <Project Sdk="..."> attribute, and CustomBeforeMicrosoftCommonProps does not reach that form — which is exactly why the equivalent setup in ktsu.Semantics only ever reached one of its projects. That repo's CLAUDE.md records the limitation but not the fix.

This is what identified #87's three findings (two S3267, one S3878) and let them be fixed before the next push.

Criterion not met

Its output matches what SonarCloud reports for the same commit — including the outstanding issue from #87.

I can't confirm this. sonarcloud.io is unreachable from this sandbox (the proxy returns 403 on CONNECT), so there's no dashboard read to calibrate against, and the one unidentified finding from #87 is still unidentified. What's here is the Semantics globalconfig with S1481 left enabled and S3267/S2699 raised because CI named them on #87 — a starting point, not a verified match. The gap is recorded in both sonar-local.globalconfig and CLAUDE.md so it doesn't read as calibrated when it isn't. Leave #88 open for whoever has dashboard access to close it.

Also

CLAUDE.md picks up the architecture refresh the merged work left it owing: the TextWriter constructors, the other scopes, the preamble helpers, the template object model, and the WriteLineNoTabs pending-tab trap. It also now states the line-ending rule for tests, which is the thing that would have caught the false green above.

Verification

  • 168/168 tests pass in Release.
  • The local Sonar run over the branch reports S4144 and S2699 only in test files byte-identical to main — nothing new from this change.

Generated by Claude Code

claude added 2 commits August 27, 2026 05:17
CodeBlocker writes through IndentedTextWriter, which terminates lines with
Environment.NewLine. That made the same calls produce different bytes on
different machines, and generated code is almost always committed, diffed, or
compared against a golden file — all of which want reproducibility more than
they want the local convention. Callers had to remember to pass NewLines.Lf,
and forgetting was silent.

Flip the default: CodeBlocker.DefaultNewLineString is NewLines.Lf, used by
every constructor and factory overload that does not take a terminator, and
substituted for a null one. NewLines.Host is still there as the opt-in for
callers who genuinely want the platform terminator.

This is a breaking change for anyone on Windows relying on the old default,
hence [major].

Tests asserted against Environment.NewLine throughout, which passes on Linux
for the wrong reason and would have hidden the Windows break entirely. They
now assert against CodeBlocker.DefaultNewLineString, and NewLineTests covers
both that the default is LF regardless of host and that the host terminator
is still reachable by asking for it.

Closes #89

Co-Authored-By: Claude <noreply@anthropic.com>
CI analyses this repository with the SonarCloud scanner, which injects the
Sonar analyzers into the compilation. A plain `dotnet build` does not run
them, so findings are invisible locally and only surface after a push — and
the bot comment links to a dashboard rather than naming them. That bit #87:
the gate passed but reported three new issues with no way to see what they
were.

Add an opt-in .sonarlint/ that nothing imports automatically:

    dotnet build -p:CustomAfterMicrosoftCommonProps=$PWD/.sonarlint/sonar-local.props

Note "After", not "Before". Every project here declares its SDK with
<Sdk Name="..." /> elements rather than the <Project Sdk="..."> attribute, and
CustomBeforeMicrosoftCommonProps does not reach that form — the reason the
equivalent setup in ktsu.Semantics only ever reached one project.

This is what identified #87's three findings (two S3267, one S3878) and let
them be fixed before the next push.

Known gap, recorded in the globalconfig and CLAUDE.md: SonarCloud reported one
new issue on #87 that this configuration does not reproduce. sonarcloud.io is
not reachable from the agent sandbox, so the rule behind it could not be
identified and the calibration criterion in #88 is not met.

CLAUDE.md also picks up the architecture refresh the merged work left it
owing — the TextWriter constructors, the other scopes, the preamble helpers,
the template object model, and the line-ending rule for tests.

Co-Authored-By: Claude <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 4d25ac6 into main Aug 27, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the claude/extract-generalize-codegen-hjt2iy branch August 27, 2026 05:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider making LF the default line terminator

2 participants