The blocking format CI job (and the matching Husky pre-commit hook) now verify changed .cs files with dotnet format whitespace . --folder --verify-no-changes --include <files> instead of the full dotnet format ErsatzTV.sln --no-restore --verify-no-changes --include <files>. The job's NuGet-cache + Restore steps are removed (folder mode needs no restore).
Why
--include only narrows which files are checked — never what gets loaded. The old recipe loaded the entire ~10-project MSBuild workspace and built a Roslyn compilation per project before checking a single line, a fixed cost regardless of how few files changed. --folder treats the tree as a plain folder of files and skips MSBuild/Roslyn entirely.
Measurements (local, this Mac, warm restore)
Command
Time
Before
dotnet format ErsatzTV.sln --no-restore --verify-no-changes --include <file> (clean)
~480s
After
dotnet format whitespace . --folder --verify-no-changes --include <file> (clean)
~0.5s
Plus the old path required a dotnet restore step; the new one does not. True CI job wall-clock will show on this PR's format run.
Coverage — unchanged, proven
Non-vacuous: folder mode exits non-zero with error WHITESPACE on an injected trailing-whitespace line and error CHARSET on a prepended UTF-8 BOM; exits 0 on a clean file. Verified through the exact CI path (mapfile + array --include, one clean + one BOM'd file → fails and pinpoints the file).
No style/analyzer coverage lost: the full gate never enforced the style pass 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 is that one naming rule, and dotnet format's default severity ignores it. The analyzers that must block (NU1904, S3981) are enforced at compile via WarningsAsErrors in Directory.Build.props, not 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 and still works).
Docs
docs/ci-cd.md → Formatting section rewritten; the obsolete #406 "env vars don't shrink format's 3.95 GiB Roslyn heap" note updated (moot — no Roslyn heap now).
docs/decisions.md → append-only entry.
Scope notes
Lane left on ubuntu-latest — the job is now seconds-long/low-memory and 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.
The #311 BOM/whitespace fix-as-you-touch convention is unchanged; this is purely about the speed of enforcing it.
Fixes #469.
## What
The blocking `format` CI job (and the matching Husky pre-commit hook) now verify changed `.cs` files with **`dotnet format whitespace . --folder --verify-no-changes --include <files>`** instead of the full `dotnet format ErsatzTV.sln --no-restore --verify-no-changes --include <files>`. The job's NuGet-cache + Restore steps are removed (folder mode needs no restore).
## Why
`--include` only narrows *which* files are checked — never what gets loaded. The old recipe loaded the entire ~10-project MSBuild workspace and built a Roslyn compilation per project before checking a single line, a fixed cost regardless of how few files changed. `--folder` treats the tree as a plain folder of files and skips MSBuild/Roslyn entirely.
## Measurements (local, this Mac, warm restore)
| | Command | Time |
|---|---|---|
| Before | `dotnet format ErsatzTV.sln --no-restore --verify-no-changes --include <file>` (clean) | **~480s** |
| After | `dotnet format whitespace . --folder --verify-no-changes --include <file>` (clean) | **~0.5s** |
Plus the old path required a `dotnet restore` step; the new one does not. True CI job wall-clock will show on this PR's `format` run.
## Coverage — unchanged, proven
- **Non-vacuous:** folder mode exits non-zero with `error WHITESPACE` on an injected trailing-whitespace line and `error CHARSET` on a prepended UTF-8 BOM; exits 0 on a clean file. Verified through the exact CI path (`mapfile` + array `--include`, one clean + one BOM'd file → fails and pinpoints the file).
- **No style/analyzer coverage lost:** the *full* gate never enforced the style pass 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` is that one naming rule, and `dotnet format`'s default severity ignores it. The analyzers that must block (`NU1904`, `S3981`) are enforced at compile via `WarningsAsErrors` in `Directory.Build.props`, not 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 and still works).
## Docs
- `docs/ci-cd.md` → Formatting section rewritten; the obsolete #406 "env vars don't shrink `format`'s 3.95 GiB Roslyn heap" note updated (moot — no Roslyn heap now).
- `docs/decisions.md` → append-only entry.
## Scope notes
- Lane left on `ubuntu-latest` — the job is now seconds-long/low-memory and *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.
- The #311 BOM/whitespace fix-as-you-touch convention is unchanged; this is purely about the *speed* of enforcing it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The blocking `format` CI job and the Husky pre-commit hook verified changed .cs
files with `dotnet format ErsatzTV.sln --no-restore --verify-no-changes
--include <files>`. `--include` only narrows *which* files are checked, never
what gets loaded: the full recipe loaded the ~10-project MSBuild workspace and
built a Roslyn compilation per project before checking a single line (~480s
locally, whole-solution). Switch both to `dotnet format whitespace . --folder
--verify-no-changes --include <files>`, which treats the tree as a plain folder
of files, skips MSBuild/Roslyn entirely (~0.5s), and needs no `dotnet restore`
(NuGet-cache + Restore steps removed).
Coverage is unchanged: folder mode reads .editorconfig and enforces exactly the
gate's purpose — whitespace + charset (BOM). Proven non-vacuous (error
WHITESPACE on a trailing-space line, error CHARSET on a prepended BOM, exit 0
clean). The full gate never enforced the style/analyzer pass either — a
warning-severity naming violation passes the full solution format (exit 0) — and
the analyzers that must block (NU1904, S3981) are enforced at compile via
WarningsAsErrors, not by this job.
Docs: ci-cd.md Formatting section + the obsolete #406 memory note; decisions.md.
fixes#469
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cold adversarial review (MERGEABLE) flagged two spots still describing the
pre-#469 heavy `format` job as current, plus a wording imprecision:
- ci-cd.md CI-lane table row: annotate `format` runtime 37s → ~0.5s (#469).
- ci-cd.md #406 memory narrative: note the 3.95 GiB Roslyn heap is now moot
(folder mode loads no workspace); api-docs remains the lane's real consumer.
- Precise the coverage mechanism in ci-cd.md + decisions.md: the naming rule
passes the full gate because naming violations have no `dotnet format` batch
code-fixer (so `--verify-no-changes` sees no change), not merely a severity
floor.
Docs-only; no workflow/hook logic change from the reviewed commit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cold adversarial review complete — MERGEABLE, no functional blocker. Detection proven non-vacuous (WHITESPACE / CHARSET-BOM / FINALNEWLINE all exit-2; clean exit-0; repo-relative --include matches workspace .; nested .editorconfig honored). Dropped restore/NuGet-cache confirmed safe (folder mode loads no MSBuild workspace). No merge-gating coverage lost — the full gate never enforced the style pass either (naming rule has no batch code-fixer), and NU1904/S3981 block at compile via WarningsAsErrors.
Review's one Medium (stale format-cost claims at ci-cd.md:106/:119) + the wording nit are fixed in follow-up commit 33657b47. That commit is docs-only — no workflow/hook logic changed from the reviewed commit eafb2e39, so no fresh code review is warranted.
Cold adversarial review complete — **MERGEABLE**, no functional blocker. Detection proven non-vacuous (WHITESPACE / CHARSET-BOM / FINALNEWLINE all exit-2; clean exit-0; repo-relative `--include` matches workspace `.`; nested `.editorconfig` honored). Dropped `restore`/NuGet-cache confirmed safe (folder mode loads no MSBuild workspace). No merge-gating coverage lost — the full gate never enforced the style pass either (naming rule has no batch code-fixer), and NU1904/S3981 block at compile via `WarningsAsErrors`.
Review's one Medium (stale `format`-cost claims at ci-cd.md:106/:119) + the wording nit are fixed in follow-up commit 33657b47. That commit is **docs-only** — no workflow/hook logic changed from the reviewed commit eafb2e39, so no fresh code review is warranted.
Review-verdict: MERGEABLE @ 33657b4753ab29a18793d4e0ec206c2ac5b94f40
CI (head 33657b47): all jobs green — Build & test, EF migration integrity, Functional E2E, API docs in sync, Formatting, CI image pin, Docs update reminder✅. Image-push skipped (main/tag only).
Sole red = decisions.md append-only — known flake. Verified my decisions.md diff vs origin/main is purely additive (one new ## block at EOF, zero deletions/modifications), so it does not violate append-only. Awaiting a selective rerun of just that job (no push, sha stays 33657b47).
All four ## Done-when boxes on #469 are now ticked. Once decisions.md append-only is green, the H6/H10 gate is satisfied for merge.
**CI (head 33657b47):** all jobs green — `Build & test`, `EF migration integrity`, `Functional E2E`, `API docs in sync`, **`Formatting`**, `CI image pin`, `Docs update reminder` ✅. Image-push skipped (main/tag only).
**Sole red = `decisions.md append-only` — known flake.** Verified my decisions.md diff vs `origin/main` is *purely additive* (one new `##` block at EOF, zero deletions/modifications), so it does not violate append-only. Awaiting a selective rerun of just that job (no push, sha stays 33657b47).
All four `## Done-when` boxes on #469 are now ticked. Once `decisions.md append-only` is green, the H6/H10 gate is satisfied for merge.
timothy
merged commit 48d632d50a into main2026-07-19 19:59:34 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #469.
What
The blocking
formatCI job (and the matching Husky pre-commit hook) now verify changed.csfiles withdotnet format whitespace . --folder --verify-no-changes --include <files>instead of the fulldotnet format ErsatzTV.sln --no-restore --verify-no-changes --include <files>. The job's NuGet-cache + Restore steps are removed (folder mode needs no restore).Why
--includeonly narrows which files are checked — never what gets loaded. The old recipe loaded the entire ~10-project MSBuild workspace and built a Roslyn compilation per project before checking a single line, a fixed cost regardless of how few files changed.--foldertreats the tree as a plain folder of files and skips MSBuild/Roslyn entirely.Measurements (local, this Mac, warm restore)
dotnet format ErsatzTV.sln --no-restore --verify-no-changes --include <file>(clean)dotnet format whitespace . --folder --verify-no-changes --include <file>(clean)Plus the old path required a
dotnet restorestep; the new one does not. True CI job wall-clock will show on this PR'sformatrun.Coverage — unchanged, proven
error WHITESPACEon an injected trailing-whitespace line anderror CHARSETon a prepended UTF-8 BOM; exits 0 on a clean file. Verified through the exact CI path (mapfile+ array--include, one clean + one BOM'd file → fails and pinpoints the file).warning-severity naming violation (local_constantsnotALL_UPPER) passed the full solution format (exit 0). The only.editorconfigrule above:suggestion/:noneis that one naming rule, anddotnet format's default severity ignores it. The analyzers that must block (NU1904,S3981) are enforced at compile viaWarningsAsErrorsinDirectory.Build.props, not by this job.Fix command for a violation
dotnet format whitespace . --folder --include <files>(the fulldotnet format ErsatzTV.sln --include <files>is a superset and still works).Docs
docs/ci-cd.md→ Formatting section rewritten; the obsolete #406 "env vars don't shrinkformat's 3.95 GiB Roslyn heap" note updated (moot — no Roslyn heap now).docs/decisions.md→ append-only entry.Scope notes
ubuntu-latest— the job is now seconds-long/low-memory and 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.🤖 Generated with Claude Code
dotnet format whitespace --folder(~480s → ~0.5s)format-job cost/memory claims (review)Cold adversarial review complete — MERGEABLE, no functional blocker. Detection proven non-vacuous (WHITESPACE / CHARSET-BOM / FINALNEWLINE all exit-2; clean exit-0; repo-relative
--includematches workspace.; nested.editorconfighonored). Droppedrestore/NuGet-cache confirmed safe (folder mode loads no MSBuild workspace). No merge-gating coverage lost — the full gate never enforced the style pass either (naming rule has no batch code-fixer), and NU1904/S3981 block at compile viaWarningsAsErrors.Review's one Medium (stale
format-cost claims at ci-cd.md:106/:119) + the wording nit are fixed in follow-up commit33657b47. That commit is docs-only — no workflow/hook logic changed from the reviewed commiteafb2e39, so no fresh code review is warranted.Review-verdict: MERGEABLE @
33657b4753CI (head
33657b47): all jobs green —Build & test,EF migration integrity,Functional E2E,API docs in sync,Formatting,CI image pin,Docs update reminder✅. Image-push skipped (main/tag only).Sole red =
decisions.md append-only— known flake. Verified my decisions.md diff vsorigin/mainis purely additive (one new##block at EOF, zero deletions/modifications), so it does not violate append-only. Awaiting a selective rerun of just that job (no push, sha stays33657b47).All four
## Done-whenboxes on #469 are now ticked. Oncedecisions.md append-onlyis green, the H6/H10 gate is satisfied for merge.