Skip to content

Add .NET support#163

Closed
SteveOltzscher wants to merge 10 commits into
maxritter:mainfrom
SteveOltzscher:feature/dotnet-support
Closed

Add .NET support#163
SteveOltzscher wants to merge 10 commits into
maxritter:mainfrom
SteveOltzscher:feature/dotnet-support

Conversation

@SteveOltzscher

@SteveOltzscher SteveOltzscher commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Add .NET / C# / Blazor support to the file-quality & TDD hooks

Summary

Extends Pilot's file_checker.py hook and rule set to first-class support .NET
development, on par with the existing Python / TypeScript / Go lanes. A
.cs/.razor edit now gets a fast single-file format check, a parsimony-aware
TDD reminder, and file-type-activated coding standards — plus opt-in C#
language-server docs.

Motivation

The hook previously had no awareness of C#: .cs/.razor edits got no
formatting feedback, no TDD nudge, and no .NET-specific standards. Naively wiring
in dotnet build/dotnet test per 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)

  • Single-file dotnet format whitespace --folder --include <file> --verify-no-changes
    — the whitespace/--folder fast path skips the MSBuild project load, restore,
    and analyzer compilation (the dominant per-edit cost) while still applying
    .editorconfig whitespace rules. Style/analyzer/compile feedback is deferred to
    the LSP and dotnet build/dotnet test.
  • Walks up to the nearest .csproj/.sln, no-ops gracefully when dotnet isn't
    on PATH, and bounds the subprocess with a timeout.

TDD detector — pilot/hooks/_checkers/tdd.py

  • .NET branch in the enforcer: suppresses the reminder when a sibling/*Tests.cs
    test 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 reminder
    only 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.
  • .NET build-output (bin//obj/) and metadata extensions (.csproj, .sln,
    .dll, …) are excluded from TDD enforcement.

Wiring — pilot/hooks/file_checker.py routes .cs/.razor to the new
checker 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, EventCallback vs StateHasChanged,
    CSS isolation, render modes, lifecycle/disposal.
  • standards-frontend.md extended to .razor/.razor.css; testing.md adds the
    C# test-naming convention.

Docshooks.md, rules.md (now "7 coding standards"), and
language-servers.md (opt-in csharp-ls, explicitly not auto-installed since
Pilot doesn't ship the .NET SDK).

Key design decisions

  • No per-edit build. Per-edit feedback is whitespace-only; correctness is
    deferred to LSP + dotnet test.
  • Conservative logic-free detection. The detector enforces on any doubt — a
    missed skip just shows a (non-blocking) reminder; a wrong skip would hide a real
    gap.
  • Opt-in C# LSP. Non-.NET users aren't burdened with a .NET toolchain.
  • Glob-based install. Both the Claude Code (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:

  1. check_dotnet now skips bin//obj//generated dirs (was asymmetric with the
    TDD path).
  2. Only dotnet format exit code 2 is treated as a formatting diff; real tool
    failures (exit 1/3) are swallowed, not mislabeled as whitespace issues.
  3. _find_dotnet_test_dirs is memoized (two call sites per edit now share one
    filesystem walk).
  4. .razor skips the format subprocess (no-op in folder mode), keeping the length
    check.
  5. Shared is_dotnet_test_project_name predicate so the format-skip and test-dir
    discovery agree.

Testing

  • New/updated suites: test_dotnet_checker.py, test_tdd_enforcer.py (+202),
    test_file_checker.py (+44) — covering format scoping, error handling, the
    logic-free detection matrix, test-dir discovery, and TDD suppression.
  • Full hook suite green for everything this PR touches; zero new failures
    introduced. (Note: 18 pre-existing failures in test_codex_skill_sync.py are
    unrelated to this feature and out of scope.)
  • ruff check / ruff format clean.
  • Verified with real unmocked execution of check_dotnet / _find_dotnet_test_dirs
    against temp project trees.

Known follow-ups (non-blocking)

  • A where T : new() constraint can produce a spurious reminder on a
    signature-only interface (benign — errs toward enforcement).
  • The .NET TDD branch is duplicated between the live _tdd_check and the
    test-only run_tdd_enforcer; worth consolidating.
  • **/*.razor is matched by both standards-frontend and standards-blazor
    intentional overlap, but worth a conscious decision.

Summary by cubic

Adds first-class .NET (C#/Blazor) support: a fast single-file dotnet format whitespace 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-core dotnet test tip for CI stability.

  • New Features

    • .NET checker (pilot/hooks/_checkers/dotnet.py): runs dotnet format whitespace --folder --include <file> --verify-no-changes scoped to the edited .cs; skips .razor, bin//obj//generated; only exit code 2 counts as a diff; labels per-file whitespace issues; no per-edit build.
    • TDD for C#: suppresses when a sibling *Tests.cs exists or nearby tests reference the symbol; conservative is_dotnet_logic_free() skips interfaces/enums/positional records/POCOs only; memoized .NET test-dir scan; shared test-project predicate.
    • Wiring (file_checker.py): routes .cs/.razor to the new checker; live TDD path uses importing-tests and logic-free detection.
    • Rules/Docs: add standards-dotnet.md and standards-blazor.md; extend frontend standards to *.razor*; add C# test naming in testing.md; opt-in csharp-ls; include dotnet test -- RunConfiguration.MaxCpuCount=1 to run the full suite single-core for CI stability.
  • Bug Fixes

    • Logic-free detector: handles where-constrained generics and explicit event add/remove; strips initializer calls so braced DTO initializers stay logic-free; .razor never marked logic-free.
    • Robustness: share skip lists with TDD; align bin//obj/ and test-project detection; .razor avoids no-op format calls; memoize .NET test-dir discovery.
    • Docs: correct hooks/LSP pages to describe the C# check as a single-file dotnet format whitespace check (not linting/type-checking).

Written for commit 9207b1d. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Documentation
    • Updated hook documentation to include an additional C# whitespace check using .NET format alongside existing linting and TDD enforcement behavior.
    • Clarified language server capabilities: real-time LSP features require Claude Code’s LSP support, while Codex CLI does not provide equivalent editor-style LSP integration; Codex still runs single-file dotnet format for C# via file_checker.py.
    • Expanded language server docs with opt-in C# LSP setup for csharp-ls.
    • Refreshed rules coverage: Pilot now ships 10 built-in rules plus 7 coding standards, including new .NET and Blazor standards.

SteveOltzscher and others added 7 commits June 16, 2026 09:09
…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.
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

@SteveOltzscher is attempting to deploy a commit to the Max Ritter Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Documentation-only updates across three feature pages to add .NET/C# coverage: hooks.md adds .NET format to the file_checker.py tool list; language-servers.md clarifies Codex CLI LSP limitations and adds an opt-in csharp-ls section; rules.md updates the coding standards count to 7 and adds .NET and Blazor table entries.

Changes

.NET and C# Documentation

Layer / File(s) Summary
Hook tool list and coding standards table updates
docs/docusaurus/docs/features/hooks.md, docs/docusaurus/docs/features/rules.md
Adds .NET format to the file_checker.py enumerated tools in hooks.md; updates the built-in coding standards count from 5 to 7 and adds .NET and Blazor rows to the file-type activation table in rules.md.
csharp-ls opt-in LSP section and Codex CLI clarification
docs/docusaurus/docs/features/language-servers.md
Adds a note clarifying that Codex CLI retains hook-based linting but lacks real-time LSP features; adds a new "C# — csharp-ls (opt-in)" section with installation steps (dotnet tool install --global csharp-ls or brew), .NET SDK 6.0+ prerequisite, and a note on compile diagnostics coverage relative to the existing hook.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add .NET support' directly reflects the main objective of extending Pilot with .NET/C#/Blazor support across documentation, file checker, and TDD detection.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 14 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread docs/docusaurus/docs/features/language-servers.md Outdated
Comment thread pilot/hooks/_checkers/tdd.py Outdated
…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.
@maxritter maxritter self-assigned this Jun 16, 2026
@maxritter

Copy link
Copy Markdown
Owner

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 dotnet format whitespace check with no per-edit build, correctness deferred to the LSP and dotnet test — is exactly the right call, and the conservative logic-free detector errs toward enforcement in all the right places.

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 @$"..." strings, a where T : new() generic constraint, and not counting symbol mentions that only appear inside comments), and simplifying the single-file format reporting. All your tests still pass.

I'll keep this open until it ships in the next release. Thanks again — genuinely great contribution. 🙏

maxritter added a commit that referenced this pull request Jun 17, 2026
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.
github-actions Bot pushed a commit that referenced this pull request Jun 17, 2026
# [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))
@maxritter maxritter closed this Jun 17, 2026
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.

2 participants