---
key: ci.format-gate-folder-mode
title: 2026-07-19 — The `format` gate runs `dotnet format whitespace . --folder`, not the full solution format (#469)
status: active
since: '2026-07-19'
supersedes: none
superseded-by: none
rule: The blocking `format` CI job (and matching pre-commit hook) runs `dotnet format whitespace . --folder --include <files>` instead of loading the full MSBuild/Roslyn solution, cutting the gate from ~480s to ~0.5s with unchanged whitespace/charset coverage.
signals: 'dotnet format, folder mode, CI format gate · paths: `.gitea/workflows` format job, `.editorconfig` · issues: #469, #406, #311'
mechanics: '`dotnet format whitespace . --folder --verify-no-changes --include <files>`'
---

The blocking `format` CI job (and the matching Husky pre-commit hook) verify changed `.cs` files with
`dotnet format whitespace . --folder --verify-no-changes --include <files>` instead of the previous
`dotnet format ErsatzTV.sln --no-restore --verify-no-changes --include <files>`.

- **Why.** `--include` narrows *which* files are checked, never what gets loaded. The full recipe loaded
  the whole ~10-project MSBuild workspace and built a Roslyn compilation per project before checking a
  single line — a fixed cost independent of how few files changed. Measured **~480s** for a whole-solution
  `dotnet format` locally (matching the issue's "7+ min"). `--folder` treats the tree as a plain folder of
  files and skips MSBuild/Roslyn entirely: **~0.5s**, and it needs no `dotnet restore`, so the job's
  NuGet-cache + Restore steps were deleted. It also drops the job's ~3.95 GiB Roslyn heap (the #406
  memory note about `format` not shrinking is now moot).
- **Coverage is unchanged, not merely "good enough".** Folder mode reads `.editorconfig` and enforces
  exactly the two things this gate exists for — **whitespace** (indent/EOL/trailing/final-newline) and
  **charset** (no UTF-8 BOM). Proven non-vacuous: exits non-zero with `error WHITESPACE` on an injected
  trailing-whitespace line and `error CHARSET` on a prepended BOM; exits 0 on a clean file. What it drops
  is the style/analyzer pass — but the *full* gate never enforced that either: a probe injecting a
  `warning`-severity naming violation (`local_constants` not `ALL_UPPER`) **passed** the full solution
  format (exit 0): the only `.editorconfig` rule above `:suggestion`/`:none` severity is that one naming
  rule, and naming violations have no `dotnet format` batch code-fixer, so `--verify-no-changes` reports
  no change regardless of severity. The analyzers that must block (`NU1904`,
  `S3981`) are enforced at compile time via `WarningsAsErrors` in `Directory.Build.props`, never by this
  job.
- **Fix command for a violation:** `dotnet format whitespace . --folder --include <files>`. The full
  `dotnet format ErsatzTV.sln --include <files>` is a superset (also applies style) and still works, so
  existing muscle memory and the #311 lore's `dotnet format --include` guidance are not broken.
- **Lane left on `ubuntu-latest`.** The job is now seconds-long and low-memory, so it could move to a
  lighter lane, but that re-touches the per-lane memory-cap accounting (#406/#604) and is a
  server-management capacity call — deliberately out of scope here.
