Backend C#
C# / ASP.NET Core / EF Core services, APIs and workers with preserved diagnostics, explicit DI lifetimes, observable async work, owned resources, safe queries and HTTP surfaces, and evidenced completion. Rule IDs BCS-01–BCS-84; NEED TO blocks on proven violations.
Set this profile
ccpp cpo profile set backend-csharpInside Claude Code, prefix the command with ! to run it in the session. Downloading a profile needs a Builder or Team plan; listing them does not.
Policies
80 policies, grouped by what they guard.
Secrets & private data 2
Credentials, keys and personal data stay out of files, logs, commands and the conversation.
bcs65-config-secretsBCS-65 · Tracked appsettings and config files reference secrets (user secrets, environment, vault) instead of literal passwords, keys or tokens
bcs72-sensitive-loggingBCS-72 · Log calls never receive passwords, tokens, API keys, authorization headers or connection strings
Authentication & authorization 4
Who may do what, from roles, privileges and permissions to CSRF, CORS and verified tokens.
bcs56-unprotected-endpointsBCS-56 · Controllers and endpoints in declared protected paths require authorization (off until protectedEndpointPaths is set)
bcs59-antiforgery-disabledBCS-59 · The user approves disabling antiforgery validation on state-changing endpoints
bcs60-credentialed-any-originBCS-60 · A CORS policy allowing credentials never accepts arbitrary origins (wildcard or accept-all predicates)
Untrusted input 3
Requests, downloads and tool output are validated before they reach SQL, a shell, eval or a deserializer.
bcs12-entity-request-bodyBCS-12 · Write endpoints bind request contracts (DTOs), not persistence entities (definition from csharp-ls; review)
bcs45-raw-sql-interpolationBCS-45 · Raw SQL APIs (FromSqlRaw, ExecuteSqlRaw, SqlCommand, Dapper) never receive interpolated or concatenated text
bcs55-unvalidated-request-bodiesBCS-55 · MVC actions binding a request body validate it before side effects ([ApiController] or a ModelState check) (review)
Weakened checks 7
Suppressions, disabled lint or CI steps and relaxed strictness need a reason or the user's approval.
bcs02-nullable-disabledBCS-02 · Project files keep nullable analysis enabled; new production projects enable it; sources do not add '#nullable disable'
bcs03-blanket-pragmaBCS-03 · No '#pragma warning disable' without a diagnostic ID (it silences every warning that follows)
bcs03-unjustified-suppressionsBCS-03 · A new '#pragma warning disable <ID>' or [SuppressMessage] says why (comment or Justification) and is restored
bcs03-weakened-diagnosticsBCS-03 · The user approves project or EditorConfig changes that silence diagnostics, lower analyzer severity or stop treating warnings as errors
bcs05-build-warningsBCS-02/BCS-04/BCS-05/BCS-06 · New nullable, dead-code, readonly and repository-style diagnostics in dotnet build output are fixed (warn)
bcs28-build-analyzer-findingsBCS-28/BCS-30/BCS-31/BCS-38 · A dotnet build reporting CA2016, CS4014, CA2012 or CA2200 on lines this turn added is fixed before going on
bcs80-weakened-test-runsBCS-80/BCS-03 · The user approves CI or project changes that drop dotnet build/test steps, filter tests out or stop treating warnings as errors in CI
Error handling 5
Failures are propagated or handled, never swallowed or turned into fake success.
bcs38-rethrow-loses-stackBCS-38 · A caught exception is rethrown with 'throw;' (or wrapped with its cause), never 'throw ex;' (CA2200)
bcs39-swallowed-exceptionsBCS-39 · No empty catch block; a catch recovers, translates at a boundary or adds context, and says so
bcs40-cancellation-as-failureBCS-40 · A catch-all around cancellable async work lets cooperative cancellation through instead of turning it into a failure (review)
bcs41-exceptions-for-validationBCS-41 · Expected invalid input is reported as a validation result, not thrown and caught in the same method
bcs42-controller-catch-allBCS-42 · Controllers do not translate every exception into their own error response; unexpected errors go through central handling (ProblemDetails)
Async & concurrency 11
Owned tasks, awaited promises, preserved cancellation and no blocking on event loops.
bcs24-blocking-waitsBCS-24 · No blocking wait on a task (.Result, .Wait(), GetAwaiter().GetResult(), Task.WaitAll) outside startup code (typed by csharp-ls)
bcs25-sync-io-in-asyncBCS-25 · Async methods call the asynchronous database, file, stream and HTTP APIs, not their blocking overloads (typed by csharp-ls)
bcs26-async-voidBCS-26 · Asynchronous methods return Task or ValueTask; async void only for event-handler signatures
bcs27-task-run-on-request-pathBCS-27 · Request handlers do not wrap work in Task.Run just to await it (it only moves the work to another pool thread)
bcs28-dropped-cancellationBCS-28 · A method holding a CancellationToken passes it to every call that accepts one (overloads resolved by csharp-ls; CA2016)
bcs29-uncancellable-entry-pointsBCS-29 · Public async I/O entry points of controllers, repositories, clients and services accept a CancellationToken
bcs30-discarded-tasksBCS-30 · A call returning Task or ValueTask is awaited, returned or explicitly supervised, never dropped (typed by csharp-ls)
bcs31-valuetask-reuseBCS-31 · A ValueTask is consumed once (awaited, .Result, .AsTask()), never twice (typed by csharp-ls; CA2012)
bcs32-await-under-lockBCS-32 · No await while holding a monitor (lock statement or Monitor.Enter/Exit); use SemaphoreSlim for async exclusion
bcs33-request-objects-in-detached-workBCS-33 · Work that is not awaited (Task.Run, StartNew, thread pool, timers) does not capture HttpContext or request objects
bcs34-concurrent-httpcontextBCS-34 · Parallel work (Parallel.ForEach, Task.WhenAll over lambdas) does not touch the same HttpContext concurrently
Performance & resource bounds 10
Timeouts, bounded retries and fan-out, reused clients, closed resources, no N+1 queries.
bcs36-undisposed-resourcesBCS-36 · Streams, connections, commands, scopes, contexts and transactions created by an operation are disposed on every exit path
bcs37-unreleased-rentalsBCS-37 · Rented buffers, semaphore slots and monitors are returned or released in a finally block
bcs46-materialize-before-filterBCS-46 · A provider-backed query is filtered, projected and limited before ToList/AsEnumerable, not after (typed by csharp-ls)
bcs47-unbounded-list-endpointsBCS-47 · HTTP endpoints do not materialize a whole table; list reads are paginated or explicitly limited (typed by csharp-ls; review)
bcs50-queries-in-loopsBCS-50 · EF Core queries and navigation loads are not issued once per item of a loop (N+1; typed by csharp-ls; review)
bcs51-tracked-read-only-queriesBCS-51 · Read-only entity queries use AsNoTracking when the method never saves (typed by csharp-ls)
bcs62-unbounded-request-bodiesBCS-62 · Request-body limits are not removed or raised to unbounded, and request bodies are not read to the end without a limit (review)
bcs68-httpclient-per-operationBCS-68 · HttpClient instances come from IHttpClientFactory or a long-lived instance, never 'new HttpClient()' per operation
bcs69-unbounded-outbound-callsBCS-69 · Outbound HTTP clients keep a finite timeout and calls are not detached from cancellation (review)
bcs70-retrying-unsafe-methodsBCS-70 · HTTP retry policies are limited to idempotent requests unless the side effect is deduplicated (review)
Architecture & boundaries 13
Declared layers and dependency directions, no import cycles, small modules, explicit dependencies.
bcs08-namespace-boundariesBCS-08 · Using directives respect the project's declared code boundaries (off until namespaceRules is set)
bcs08-project-boundariesBCS-08 · Project and package references respect the declared project boundaries (off until projectReferenceRules is set)
bcs09-project-reference-cyclesBCS-09 · A new ProjectReference does not close a cycle in the project graph (followed three references deep)
bcs10-transport-in-domainBCS-10 · Declared pure-domain code does not import ASP.NET Core, HTTP or persistence namespaces (off until domainPaths is set)
bcs13-public-iqueryableBCS-13 · Public application contracts do not return IQueryable outside an approved query abstraction
bcs17-service-locatorBCS-17 · Business code receives dependencies through its constructor, not GetService/GetRequiredService at run time
bcs18-scoped-in-singletonBCS-18 · A hosted service (a singleton) does not take a DbContext in its constructor; it creates a scope or a context per operation (type from csharp-ls)
bcs19-build-service-providerBCS-19 · Registration code does not build a second service provider (BuildServiceProvider) while configuring services
bcs20-disposing-injected-servicesBCS-20 · Classes do not dispose services the container injected into them (the container owns their lifetime)
bcs21-root-provider-in-hosted-servicesBCS-21 · A hosted service resolves services from a per-operation scope, not from the root provider
bcs23-scoped-in-middlewareBCS-23 · Conventional middleware takes scoped services (a DbContext) in InvokeAsync, not its constructor (type from csharp-ls)
bcs74-wall-clockBCS-74 · Business logic takes time from an injected TimeProvider, not DateTime.Now/UtcNow, so expiry and scheduling are testable
HTTP & APIs 4
Request and response contracts, schemas, status codes, error bodies and published API surfaces.
bcs58-middleware-orderBCS-58 · The pipeline keeps UseRouting, UseCors, UseAuthentication, UseAuthorization and endpoint mapping in the required order
bcs61-exception-details-exposedBCS-61 · Production error responses carry no stack traces or raw exceptions; the developer exception page is Development-only
bcs63-headers-after-response-startBCS-63 · Status code and headers are not set after the response body started (WriteAsync, CopyToAsync, SendFileAsync)
bcs64-sync-request-bodyBCS-64 · Request.Form and the request body are read asynchronously (ReadFormAsync), not synchronously; AllowSynchronousIO stays off
Databases & queries 4
Schemas, queries, transactions and the ORM used as intended.
bcs43-dbcontext-lifetimeBCS-43 · A DbContext is never static or registered as a singleton; each unit of work gets its own (type from csharp-ls)
bcs44-overlapping-dbcontext-operationsBCS-44 · Task.WhenAll does not run two EF Core operations on the same DbContext concurrently (typed by csharp-ls)
bcs48-unstable-paginationBCS-48 · Offset pagination (Skip) on a database query orders by a declared unique key (typed by csharp-ls)
bcs52-multiple-savechangesBCS-52 · A method calling SaveChanges more than once runs the writes in one transaction (or one SaveChanges) (review)
Configuration 2
Settings read once at a boundary, validated, with units and without unsafe defaults.
bcs66-configuration-in-business-codeBCS-66 · Business code takes typed options (IOptions<T>), not IConfiguration with ad-hoc keys; composition roots and options classes may
bcs67-unvalidated-optionsBCS-67 · Options bound from configuration are validated at startup (ValidateOnStart), not on first use
Logging & observability 1
Structured logs, bounded metric labels, no debug prints left in production code.
bcs71-interpolated-log-templatesBCS-71 · Log messages use constant templates with named placeholders, not interpolated or concatenated strings (CA2254)
Dependencies & toolchain 1
Packages change through the package manager, lockfiles stay consistent and the project's tools are used.
bcs07-package-additionsBCS-07 · A new NuGet package names the changed feature or build tooling that uses it (review)
Tests 8
Tests assert behavior, stay deterministic and are not removed or skipped silently.
bcs54-inmemory-provider-testsBCS-54 · Tests of provider-specific queries and constraints run against the production database engine, not EF Core InMemory
bcs75-untested-changesBCS-75/BCS-76 · A turn that changes production C# code changes tests too, or says why none applies (review, once per stop)
bcs76-assertion-free-testsBCS-76 · Tests assert an observable outcome; no test without assertion and no tautological assertion (Assert.True(true))
bcs77-direct-controller-testsBCS-77 · HTTP contracts (routing, binding, authorization, errors) are tested through the application host, not by calling controllers directly
bcs78-static-test-stateBCS-78 · Test classes do not share mutable static state between tests (order and parallelism dependence) (review)
bcs79-sleep-in-testsBCS-79 · Asynchronous tests wait for events or bounded conditions, not fixed sleeps (Thread.Sleep, Task.Delay)
bcs80-removed-testsBCS-80 · The user approves edits that remove test methods or assertions from a test file
bcs80-skipped-testsBCS-80 · The user approves newly skipped or ignored tests
Completion evidence 5
A turn ends with its checks run and passing; claims of success need proof after the last edit.
bcs-turn-baselineRecords the work tree when a prompt starts, the baseline of the end-of-turn and build-output checks (BCS-01, BCS-05, BCS-28, BCS-75)
bcs01-new-compile-errorsBCS-01 · Claude does not end a turn with compiler errors it introduced in C# sources or the files that use them (csharp-ls)
bcs81-placeholdersBCS-81 · Production code ships implemented behavior, not NotImplementedException, TODO-only bodies or TODO stub returns
bcs83-completion-claimsBCS-83 · A final message claiming a passing build or tests needs a successful dotnet run after Claude's last edit
bcs83-evidence-ledgerBCS-83 · Records, per session, Claude's edits and the dotnet build, test or format runs that succeeded after them