Skip to content

Migrate onto ktsu.CodeBlocker 1.3.0 and retire the line-ending rewrite - #190

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

Migrate onto ktsu.CodeBlocker 1.3.0 and retire the line-ending rewrite#190
matt-edmondson merged 2 commits into
mainfrom
claude/extract-generalize-codegen-hjt2iy

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

The last two tasks of #181, now that ktsu-dev/CodeBlocker#87 has merged and published ktsu.CodeBlocker 1.3.0.

Closes #184, closes #187. That completes the epic's task list except the still-open question of where the Roslyn-side toolkit finally lives.

Emit through the package's template model (#187)

The local Templates/ copy is gone; the generators describe their output with ktsu.CodeBlocker.Templates.

Mostly mechanical — the collection properties are read-only, so object initializers use collection-initializer syntax, and a type's declaration keyword comes from ClassTemplate.Kind rather than free text in Keywords. WithComments/WithInterfaces cover the few call sites holding a prebuilt sequence. TypeKind needs a using alias because Microsoft.CodeAnalysis declares one too.

One substantive change: every BodyFactory wrote a leading space (" => Create(...)") because the old model spliced the body raw with no separator. The package model supplies that separator itself, so the leading space became a double space in the output — removed at all 16 sites.

The output diff is large, and all of it is the model doing its job

181 files change. Representative:

-internal static class ConversionConstants{
+internal static class ConversionConstants
+{
-	public static Mass<T> FromKilogram(T value) => Create(...);
-/// <summary>
+	public static Mass<T> FromKilogram(T value) => Create(...);
+
+	/// <summary>

Types open their brace on its own line and close with } instead of };; members are separated by a blank line; and doc comments sit at the member's indent rather than flush against column zero, which is where every /// <summary> after the first member used to land.

PropertyTemplateTests is deleted rather than ported — it covered the local PropertyTemplate, and ktsu.CodeBlocker now has much broader coverage of the same behaviour, including accessor cases this model could not express. WriteFileHeader/WriteSourceFile go back to protected; they were narrowed only because SourceFileTemplate was local.

Pin the terminator, drop the rewrite (#184)

GeneratedSource existed only to rewrite every line ending after the fact, because IndentedTextWriter uses Environment.NewLine and committed output has to be byte-identical wherever it was produced. 1.3.0 lets the terminator be pinned at construction, so the rewrite is unnecessary and the type is gone.

It is pinned to LF, not CRLF — the old pass was working from a stale premise. Its own comment justified CRLF on the grounds that .editorconfig said end_of_line = crlf and .gitattributes said *.cs text eol=crlf. Neither has been true since the LF migration in #157 changed them to end_of_line = lf and * text=auto eol=lf, and nothing updated the generator. Git had been quietly normalising the difference on every commit ever since:

stored blob:  UTF-8 (with BOM) text
working tree: UTF-8 (with BOM) text, with CRLF line terminators

That mismatch is the source of the permanent CRLF will be replaced by LF warnings anyone touching this repo sees. So the generated files are byte-identical as stored — a plain git diff over Generated/ is empty — and the working tree now agrees with the repository instead of contradicting it.

That also removes the reason verify-generated ran on windows-latest, which its comment gave as matching git's autocrlf to the committed CRLF. It runs on ubuntu-latest now; the alias-props step uses pwsh, which Linux runners have. I verified the gate's actual command (git diff --exit-code) passes on Linux.

Verification

  • git diff over Semantics.Quantities/Generated/ is empty after a clean rebuild — the drift gate's own check, run on Linux.
  • 1015 passed, 92 failed. The 92 are the same Windows-path tests that fail on main under Linux (see the analysis in Shape the generator infrastructure for extraction #188); the −4 against the previous run is exactly the deleted PropertyTemplateTests.
  • Built with EnforceCodeStyleInBuild on, so the IDE rules that only surface in CI are clean.

Notes for the reviewer

  • Local workarounds are not committed: Directory.Packages.props keeps Roslyn at 5.9.0 (this container's SDK ships 5.0 and cannot load the generator otherwise), and the ktsu.Sdk.Analyzers drop is a command-line flag. That means the KTSU rules did not run locally — worth a glance at the first CI result.
  • CLAUDE.md and docs/physics-generator.md gain the template-model conventions: Kind supplies the declaration keyword, collections use collection-initializer syntax, and a BodyFactory must not prefix its expression body with a space.

Generated by Claude Code

claude added 2 commits August 27, 2026 04:47
The C# syntax template model moved to ktsu.CodeBlocker 1.3.0, so the local copy
under Templates/ goes away and the generators describe their output with the
package's model instead.

The migration is mostly mechanical - the collection properties are read-only
now, so object initializers use collection-initializer syntax, and a type's
declaration keyword comes from ClassTemplate.Kind rather than from free text in
Keywords. WithComments and WithInterfaces cover the few call sites that hold a
prebuilt sequence, which a read-only property cannot be assigned. TypeKind needs
a using alias because Microsoft.CodeAnalysis declares one too.

One substantive change: every BodyFactory used to write a leading space (" =>
Create(...)") because the old model spliced the body raw with no separator. The
package model supplies that separator itself, so the leading space became a
double space in the output and is removed at all 16 sites.

The committed output under Semantics.Quantities/Generated/ changes across 181
files, and all of it is the model doing its job:

  -internal static class ConversionConstants{
  +internal static class ConversionConstants
  +{

Types now open their brace on its own line and close with "}" instead of "};",
members are separated by a blank line, and doc comments sit at the member's
indent instead of at column zero - the previous output had every "/// <summary>"
flush against the left margin directly after the preceding member.

PropertyTemplateTests is deleted rather than ported: it covered the local
PropertyTemplate, and ktsu.CodeBlocker now carries far better coverage of the
same behaviour, including the accessor cases this model could not express.

WriteFileHeader and WriteSourceFile go back to protected - they were narrowed to
private protected only because SourceFileTemplate was local, and it is public in
the package.

Test suite: 1015 passed, 92 failed - the same 92 Windows-path tests that fail on
main under Linux. The -4 against the previous run is exactly the deleted
PropertyTemplateTests.

Closes #187

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sagQjzFv3cnFNM3G271TJ
…inor]

GeneratedSource existed only to rewrite every line ending after the fact,
because IndentedTextWriter uses Environment.NewLine and committed generator
output has to be byte-identical wherever it was produced. ktsu.CodeBlocker 1.3.0
lets the terminator be pinned at construction, so the rewrite is unnecessary and
the type is gone; GeneratedSource.Add was a normalization wrapper, so call sites
use context.AddSource directly.

It is pinned to LF, not CRLF. The pass being removed rewrote everything to CRLF
on the grounds - stated in its own comment - that .editorconfig said
end_of_line = crlf and .gitattributes said *.cs text eol=crlf. Neither is true
any more: the LF migration in #157 changed them to end_of_line = lf and
* text=auto eol=lf, and nothing updated the generator. Git had been quietly
normalising the difference on every commit ever since - the stored blobs are LF
while every working tree carried CRLF, which is where the permanent "CRLF will
be replaced by LF" warnings came from.

So the generated files are byte-identical as stored (a plain git diff over
Generated/ is empty), and now the working tree matches the repository instead of
disagreeing with it.

That also removes the reason verify-generated ran on windows-latest, which its
comment gave as matching git's autocrlf to the committed CRLF. It runs on
ubuntu-latest now; the alias-props step uses pwsh, which Linux runners have.

Test suite unchanged: 1015 passed, 92 failed - the same Windows-path tests that
fail on main under Linux.

Closes #184

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sagQjzFv3cnFNM3G271TJ

Copy link
Copy Markdown
Contributor Author

Test on ubuntu-latest is red, and it is not this PR's

Same situation as #188, restated here because this is a separate PR:

count
main @ 3b40272 (run) ❌ 92 failed, 1006 passed
this PR @ 55f4f4b (run) ❌ 92 failed, 1015 passed

Identical failure count, and identical to what I measured locally before pushing. All 92 are Semantics.Paths tests that assume Windows path semantics — drive letters, \ separators, the 259-character limit — and they have been red on main since 8b9df02 added Linux to the test matrix. Test on windows-latest is unaffected.

The total drops from 1112 to 1108 because this PR deletes PropertyTemplateTests: it covered the local PropertyTemplate, which has moved to ktsu.CodeBlocker along with much broader coverage of the same behaviour. 1015 = 1019 − 4 accounts for it exactly, so no test that was passing has stopped.

Not fixing it here — no fix exists to port, and reworking the path suite's platform assumptions across ~a dozen files is unrelated to this change and would bury the diff. The shape of that fix is written up in my comment on #188; it wants its own issue. Not re-running the job: it reproduces identically and is already reproduced on main.


Worth noting from this run: Generated files up to date passed on ubuntu-latest

That job moved off windows-latest in this PR, and this is its first Linux run. Passing confirms both halves of #184 at once — the generators' LF pin agrees with * text=auto eol=lf, and the drift gate no longer depends on the runner's autocrlf. Had the CRLF-vs-LF reading been wrong, this is precisely the check that would have caught it.


Generated by Claude Code

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.

Migrate Semantics.SourceGenerators onto the extracted code-generation packages Retire GeneratedSource once CodeBlocker emits deterministic newlines

2 participants