Add .NET support#163
Conversation
…ile checker) - Conservative C# logic-free detector: TDD enforcement skips pure data models/interfaces/enums; enforces on any method/accessor/expression/ctor body, default interface method, .razor, or unreadable file. - Single-file .NET checker: drop per-edit dotnet build, scope dotnet format to the edited file via --include. - Trim standards-dotnet.md (test-framework-agnostic); split Blazor into standards-blazor.md (paths .razor*); de-Blazor standards-frontend.md. - Remove .NET content from always-loaded testing.md (keep only C# naming). - Docs: rules.md (7 standards + .NET/Blazor rows), hooks.md, language-servers.md (opt-in csharp-ls LSP). - Tests: add test_dotnet_checker.py + detector/suppression tests.
Remediates all 8 findings from the branch code review of the .NET dev
support feature (file-checker hook + TDD detector):
- is_dotnet_logic_free: strip initializer calls before the ')' '{' body
check so braced DTO initializers (= new() { ... }) stay logic-free
- file_checker: wire has_test_importing_module_dotnet into the live TDD
path so C# modules covered by importing tests are no longer false-flagged
- _find_dotnet_test_dirs: require a dotted/PascalCase boundary so dirs
like latest/contest are not misread as test projects
- should_skip: scope bin/obj exclusion to .cs/.razor so non-.NET source
under bin/ still gets TDD enforcement
- dotnet format: use `whitespace --folder` to skip the MSBuild project
load (dominant per-edit cost); drop dead _find_nearest_csproj
- _format_dotnet_issues: report whitespace issues, not "N files" (run is
--include-scoped to one file)
- check_dotnet: match test-project dirs by path segment (.Tests/.Test),
not substring, to stop over-skipping siblings like MyApp.TestData
- _run_dotnet_format: drop param threading; replace bare except with
(OSError, subprocess.SubprocessError) + debug log
Tests: +7 cases. Full hooks suite 695 passed; ruff clean. Verified live
on .NET 10 SDK (whitespace --folder ~1.8x faster, --include scoping,
importing-test suppression, issue labelling).
- check_dotnet now reuses should_skip() so the format path skips bin/obj/ generated dirs like the TDD path (no dotnet format on generated .cs). - Treat only exit code 2 (CheckFailedExitCode) as a formatting diff; swallow real tool failures (exit 1/3) instead of mislabeling them as whitespace issues. - Skip the dotnet format subprocess for .razor (no-op in folder mode); keep the file-length check. - Extract shared is_dotnet_test_project_name predicate so check_dotnet's skip and _find_dotnet_test_dirs discovery agree (IntegrationTests/FooTest now consistent). - Memoize _find_dotnet_test_dirs so the two call sites per edit share one walk. Adds 4 behavior tests. Full hook suite: 0 new failures.
|
@SteveOltzscher is attempting to deploy a commit to the Max Ritter Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughDocumentation-only updates across three feature pages to add .NET/C# coverage: Changes.NET and C# Documentation
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
2 issues found across 14 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…ccessors
- Fix generic-constrained method detection: allow optional `where` clause between `)` and `{` in the body regex pattern so constraints don't suppress the TDD reminder
- Fix explicit event accessor detection: extend the accessor alternation from `(?:get|set|init)` to include `add|remove`, since explicit event accessors carry executable logic
- Add regression tests: `test_generic_constrained_method_body_enforces` and `test_event_accessor_body_enforces` to prevent future misclassification
The file_checker.py hook only runs a single-file 'dotnet format whitespace' check for C#, but the language-servers and hooks docs listed 'dotnet format' alongside ruff/basedpyright/ESLint/go vet under 'linting and type-checking'. dotnet format is a whitespace formatter, not a linter/type-checker, so this overstated Codex's C# capabilities and contradicted the C# section (which correctly states the hook runs a dotnet format check only). Separate the C# whitespace check from the linting/type-checking group in both docs to match the actual check_dotnet behavior.
|
Thank you for this, @SteveOltzscher — it's excellent, well-scoped work, and I really appreciate the care that went into it. I went through it in depth and it's in great shape. The deliberately lightweight design — a single-file I've integrated it along with a few small review follow-ups: syncing the marketing site's language list to mention .NET/C#, tightening a couple of TDD-heuristic edge cases (verbatim-interpolated I'll keep this open until it ships in the next release. Thanks again — genuinely great contribution. 🙏 |
Merge PR #163 by SteveOltzscher: first-class .NET (C#/Blazor) support — single-file dotnet format whitespace check, C#-aware TDD reminder, and .NET/Blazor coding standards. Plus marketing-site doc sync for the new language support.
# [9.6.0](v9.5.1...v9.6.0) (2026-06-17) ### Bug Fixes * address .NET support code-review findings ([550db6f](550db6f)) * harden .NET file checker (code-review findings 1,2,3,6,7) ([d88f550](d88f550)) * **tdd:** fix C# logic-free heuristic for generic methods and event accessors ([55875cc](55875cc)) * unify context window to single opusplan control and harden .NET hooks ([01d5257](01d5257)) ### Features * add .NET/C# support to file-quality & TDD hooks ([#163](#163)) ([a3f3d22](a3f3d22)), closes [C#-aware](https://github.com/C/issues/-aware) * optimize .NET dev support (rule scoping, TDD detector, single-file checker) ([40140d4](40140d4))
Add .NET / C# / Blazor support to the file-quality & TDD hooks
Summary
Extends Pilot's
file_checker.pyhook and rule set to first-class support .NETdevelopment, on par with the existing Python / TypeScript / Go lanes. A
.cs/.razoredit now gets a fast single-file format check, a parsimony-awareTDD reminder, and file-type-activated coding standards — plus opt-in C#
language-server docs.
Motivation
The hook previously had no awareness of C#:
.cs/.razoredits got noformatting feedback, no TDD nudge, and no .NET-specific standards. Naively wiring
in
dotnet build/dotnet testper edit would be far too slow (MSBuild load +restore + analyzers on every keystroke-level change), so this PR takes a
deliberately lightweight approach.
What's included
File checker —
pilot/hooks/_checkers/dotnet.py(new)dotnet format whitespace --folder --include <file> --verify-no-changes— the
whitespace/--folderfast path skips the MSBuild project load, restore,and analyzer compilation (the dominant per-edit cost) while still applying
.editorconfigwhitespace rules. Style/analyzer/compile feedback is deferred tothe LSP and
dotnet build/dotnet test..csproj/.sln, no-ops gracefully whendotnetisn'ton
PATH, and bounds the subprocess with a timeout.TDD detector —
pilot/hooks/_checkers/tdd.py.NETbranch in the enforcer: suppresses the reminder when a sibling/*Tests.cstest exists, when a nearby test references the edited symbol (parsimony —
integration tests count), or when the file is provably logic-free.
is_dotnet_logic_free()— a conservative C# detector that skips the reminderonly for interfaces, enums, positional records, and POCO/DTOs
(auto-properties/fields/consts), enforcing on any sign of executable logic.
Includes a hand-rolled comment/string/char-literal stripper so braces and
keywords inside strings/comments don't drive detection.
.NETbuild-output (bin//obj/) and metadata extensions (.csproj,.sln,.dll, …) are excluded from TDD enforcement.Wiring —
pilot/hooks/file_checker.pyroutes.cs/.razorto the newchecker and TDD branch.
Rules
standards-dotnet.md(new) — dotnet CLI, nullable/analyzers/TreatWarningsAsErrors,async/HTTP/exception/logging reminders, ASP.NET patterns, and a substitution-based
testing/mocking table.
standards-blazor.md(new) — components,EventCallbackvsStateHasChanged,CSS isolation, render modes, lifecycle/disposal.
standards-frontend.mdextended to.razor/.razor.css;testing.mdadds theC# test-naming convention.
Docs —
hooks.md,rules.md(now "7 coding standards"), andlanguage-servers.md(opt-incsharp-ls, explicitly not auto-installed sincePilot doesn't ship the .NET SDK).
Key design decisions
deferred to LSP +
dotnet test.missed skip just shows a (non-blocking) reminder; a wrong skip would hide a real
gap.
copytree) and Codex (iterdir)installers pick up the new rule files automatically — no registry edits.
Hardening pass (review follow-up)
A code review of the branch surfaced robustness gaps, fixed in the final commit:
check_dotnetnow skipsbin//obj//generated dirs (was asymmetric with theTDD path).
dotnet formatexit code2is treated as a formatting diff; real toolfailures (exit 1/3) are swallowed, not mislabeled as whitespace issues.
_find_dotnet_test_dirsis memoized (two call sites per edit now share onefilesystem walk).
.razorskips the format subprocess (no-op in folder mode), keeping the lengthcheck.
is_dotnet_test_project_namepredicate so the format-skip and test-dirdiscovery agree.
Testing
test_dotnet_checker.py,test_tdd_enforcer.py(+202),test_file_checker.py(+44) — covering format scoping, error handling, thelogic-free detection matrix, test-dir discovery, and TDD suppression.
introduced. (Note: 18 pre-existing failures in
test_codex_skill_sync.pyareunrelated to this feature and out of scope.)
ruff check/ruff formatclean.check_dotnet/_find_dotnet_test_dirsagainst temp project trees.
Known follow-ups (non-blocking)
where T : new()constraint can produce a spurious reminder on asignature-only interface (benign — errs toward enforcement).
.NETTDD branch is duplicated between the live_tdd_checkand thetest-only
run_tdd_enforcer; worth consolidating.**/*.razoris matched by bothstandards-frontendandstandards-blazor—intentional overlap, but worth a conscious decision.
Summary by cubic
Adds first-class .NET (C#/Blazor) support: a fast single-file
dotnet formatwhitespace check for.cs, a C#‑aware TDD reminder, and new .NET/Blazor standards. Docs clarify the C# path is formatting-only and add a single-coredotnet testtip for CI stability.New Features
.NETchecker (pilot/hooks/_checkers/dotnet.py): runsdotnet format whitespace --folder --include <file> --verify-no-changesscoped to the edited.cs; skips.razor,bin//obj//generated; only exit code2counts as a diff; labels per-file whitespace issues; no per-edit build.*Tests.csexists or nearby tests reference the symbol; conservativeis_dotnet_logic_free()skips interfaces/enums/positional records/POCOs only; memoized .NET test-dir scan; shared test-project predicate.file_checker.py): routes.cs/.razorto the new checker; live TDD path uses importing-tests and logic-free detection.standards-dotnet.mdandstandards-blazor.md; extend frontend standards to*.razor*; add C# test naming intesting.md; opt-incsharp-ls; includedotnet test -- RunConfiguration.MaxCpuCount=1to run the full suite single-core for CI stability.Bug Fixes
where-constrained generics and explicit eventadd/remove; strips initializer calls so braced DTO initializers stay logic-free;.razornever marked logic-free.bin//obj/and test-project detection;.razoravoids no-op format calls; memoize .NET test-dir discovery.dotnet formatwhitespace check (not linting/type-checking).Written for commit 9207b1d. Summary will update on new commits.
Summary by CodeRabbit
.NET formatalongside existing linting and TDD enforcement behavior.dotnet formatfor C# viafile_checker.py.csharp-ls.