Prompt file imported from NCodeGroup/Identity (
.github/prompts/audit-conventions.prompt.md). Copyright stays with the author.
Run a full convention audit of this repository against its authoritative rules — the scoped instruction files
csharp-production.instructions.md and
csharp-testing.instructions.md, mirrored for orientation in
AGENTS.md. Produce a ranked findings report. Do not change code unless I explicitly ask —
audit first, offer fixes second.
1. Load the rules
Read the production and testing instruction files in full. They are authoritative. Every rule is tagged ✅ tool-enforced / 👁 review-only / ⏳ rolling out. The ✅ subset is validated by the build, so spend your manual effort on the 👁 rules.
2. Confirm the machine-enforced subset is green
Run the Definition of Done — it covers CSharpier, the analyzers, banned APIs + sync-over-async, and CA1708:
./build/dod.ps1
Then verify tested-or-excluded (§7): every non-[ExcludeFromCodeCoverage] type should be exercised by tests.
Remove-Item -Recurse -Force ./TestResults -ErrorAction SilentlyContinue
dotnet test NCode.Identity.slnx -c Debug --collect:"XPlat Code Coverage" --results-directory ./TestResults --nologo | Out-Null
$seen = @{}
Get-ChildItem ./TestResults -Recurse -Filter coverage.cobertura.xml | ForEach-Object {
[xml]$x = Get-Content $_.FullName
foreach ($c in $x.coverage.packages.package.classes.class) {
$n = $c.name; $lr = [double]$c.'line-rate'
if (-not $seen.ContainsKey($n) -or $lr -gt $seen[$n]) { $seen[$n] = $lr }
}
}
$under = $seen.GetEnumerator() | Where-Object { $_.Value -lt 1.0 }
"classes: $($seen.Count); below 100%: $(@($under).Count)"
$under | Sort-Object Value, Name | ForEach-Object { "{0,6:P0} {1}" -f $_.Value, $_.Key }
Any class that is neither tested nor annotated [ExcludeFromCodeCoverage] is a violation. (Coverlet reports
async bodies as separate <...>d__N entries, and only assemblies referenced by a test project appear — a package with
no .Tests project is invisible, which is itself a finding.)
3. Sweep production .cs for the review-only rules
Scan production code only (test projects are exempt from several rules). Check each rule set:
- §1 language/async — every own async method returns
ValueTask/ValueTask<T>, has anAsyncsuffix, and takesCancellationTokenlast.Taskis allowed only at framework boundaries (middlewareInvokeAsync, minimal-API handlers, mediator handlers). Collection expressions,constfor literals, andu8for UTF-8 bytes. Fields only forref/Interlocked/fixed/stackalloc; everything else an auto-property. Constants/statics at the top. - §2 public API — vocabularies are open by default (ADR-0150): every
enummust pass the three-part closed test (members fixed outside the package's control or by its own operation contract; no other package could need a member; a new member would need a library change anyway), and an open vocabulary ispublic const stringnames plus registered descriptors/strategies — never[Flags], never aswitch/mask encoding relationships among members; no secret material on the surface; no behavior-selectingbool/enumflag arguments; data/options/event types aresealedwith concrete collections; no cross-assemblyInternalsVisibleTo(only*Tests+DynamicProxyGenAssembly2). - §3 organization — one type per file; no nested types except a private type closing over the enclosing generic
parameter or a nested
staticconstant-vocabulary class; the single canonical impl ofIFooisDefaultFoo; namespaces flat per package. - §6 clean code — small single-purpose methods, ≤3 params, Command-Query Separation, no empty
catch, noasync void, intention-revealing names, no commented-out code, no repeated magic strings. - §7 testability — no captured request/ambient context in stored state (a read-over-ambient type is a live view
over
IHttpContextAccessor); service-location only in the sanctioned singleton→scoped case; privates covered transitively; tested-or-excluded.
Fast mechanical checks (grep production .cs, adapt as needed):
- Nested types: a
class/struct/interface/enum/recorddeclaration indented inside another type. enumon a public surface (apply the ADR-0150 closed test to each) ·[Flags]· aswitchover a vocabulary's members that encodes their relationships ·IList<returns ·boolparameters that switch behavior ·async void· emptycatch·.Result/.Wait()/.GetAwaiter().GetResult()(banned) ·Newtonsoft(banned).- Primary-constructor candidates (§1): grep
^\s+(public|internal|protected)\s+[A-Z]\w*\(— one identifier immediately before(is a constructor (a method hastype name(). Any type whose single ctor body is onlyX = x;assignments (or param-derived initializers) should be a primary constructor — including anabstractclass with aprotectedpure-assignment ctor. Non-candidates: multiple ctors /this()delegation, a body with a guard/loop/branch or side-effecting call, a shared intermediate local used by ≥2 members, exceptions, records. - Unbuffered stored collections (§2): a ctor/method that assigns a collection parameter (or DI
IEnumerable<T>) straight to a field/property with no.ToArray()/.ToFrozenSet()/.ToFrozenDictionary()/[.. x]. - Before sealing, confirm it's a leaf (§2): grep
:\s*TypeName\bfor real inheritance — aTypeName? Bar { get; }property is composition (has-a), not a base class, and is still sealable. RequestServices/GetService/GetServices— confirm each site is the sanctioned singleton service-location exception or[ExcludeFromCodeCoverage]framework glue.
Exhaustive sweeps are deterministic. A claim that a whole class of defect is absent (for example "no get-only
property's summary says Gets or sets") comes from a script or grep that lists every candidate with file:line, never
from an exploration subagent — those under-report and will answer "0 / clean". PowerShell -match is case-insensitive;
use -cmatch for casing checks (a summary that starts lower-case, "a id" vs "an ID"). The documentation defects the
compiler reports as errors — CS1570 (malformed XML), CS1572 / CS1573 (a wrong or missing <param>), CS1574 (a
bad cref) — are absent once the Definition of Done passes, so spend manual effort on the review-only classes: a
Gets or sets summary that disagrees with its accessor, terse or copy-pasted summaries, and grammar. To compare a
summary with its accessor, read past the /// and attribute lines to the declaration, and classify { get; },
{ get; init; }, and => as read-only and set; as mutable.
4. Report
Deliver a ranked report — 🔴 violation / 🟡 worth fixing / 🟢 minor or conscious trade-off — each with the file, the rule it touches, and a one-line fix. Affirm what is clean; if the codebase is compliant, say so plainly and don't manufacture nitpicks. Offer to implement fixes; only edit code if I say yes.
