Clarify Copilot guidance for framework-only invariants vs user-reachable validation
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Documentation
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- csharp
- Domain
- documentation
Research direction
Start with .github/copilot-instructions.md and read the Assertions and validation context suggested in the issue, along with docs/CrossCuttingGuidance.md. Review the cited #68961 change and EndpointHtmlRenderer.InitializeStandardComponentServicesAsync example for the distinction between framework-only invariants and reachable input. Done means adding concise guidance that preserves assertions and Release behavior by default while retaining runtime validation for supported external inputs.
Written by the indexing model from the issue text.
Description
Summary
Update .github/copilot-instructions.md to require classifying who can violate a precondition before changing an internal assertion into a runtime exception. Preserve framework-only assertions and their release behavior unless there is evidence that application-controlled input can reach the condition or an explicit contract calls for a runtime failure.
Motivation and goals
In #68961, commit 6c10ebb2a713b616cdc4d4afcccc3c7d6fdfef28 replaced a Debug.Assert in EndpointHtmlRenderer.InitializeStandardComponentServicesAsync with an InvalidOperationException, and added a test expecting the exception. This condition describes a framework-controlled call-order invariant: the first initialization call owns endpoint-specific data. The existing assertion catches a framework developer mistake during development; in release, a later call reused the cached task. The new throw instead introduces a runtime failure path for that invariant.
The cross-cutting guidance correctly asks for exceptions for invalid public/external input, but the Copilot instructions do not distinguish such validation from an invariant exclusively controlled by framework code. An unqualified preference for surfacing errors can therefore encourage this change. The goal is to make the ownership and reachability check explicit, not to discourage legitimate runtime validation.
In scope
- Add a concise rule under Task Scope and Completion or a dedicated Assertions and validation section of
.github/copilot-instructions.md: before adding a throw or replacing aDebug.Assert, trace the callers and identify whether application/user input can cause the violation, or whether it requires framework code to break its own call-order/state invariant. - For framework-only invariants, retain the development-time assertion and the existing release behavior by default. Do not introduce a new runtime throw or a test that codifies it solely to make the invariant fail loudly in release. If a runtime failure is intentional, explain the reachable scenario and why the behavior change is needed.
- For public/API arguments, configuration, external input, and supported application-driven invalid states, keep runtime validation and actionable exceptions.
internalvisibility by itself does not prove that a condition is framework-only. - When reviewing a replacement assertion, compare Debug and Release behavior and use a test at a faithful caller boundary to establish reachability rather than treating a direct call to an internal helper with impossible arguments as proof of a user scenario.
Suggested instruction text:
Distinguish framework-controlled invariants from invalid states reachable through supported application/user input before adding an exception or replacing
Debug.Assert. Trace the real callers and contract. For a condition that only framework code can violate, preserve the assertion and existing Release behavior unless a deliberate runtime behavior change is justified; do not add a throw merely to harden that internal path. Keep runtime exceptions for invalid public/external inputs and supported application-controlled states. A direct test invocation of an internal method does not, by itself, establish user reachability.
Out of scope
- Changing shipping code or tests in #68961 as part of this documentation issue.
- A blanket ban on exceptions in internal methods, or removal of existing validation for reachable invalid inputs.
Risks / unknowns
An overbroad reading could suppress needed runtime checks in internal code reached from public APIs. The instruction must key off the source of the invalid state and supported call paths, not method accessibility. It should allow a deliberate exception when a concrete reachable scenario and intended behavior are documented.
Examples
In EndpointHtmlRenderer.InitializeStandardComponentServicesAsync, a second framework-owned initialization supplying endpoint-specific values should remain a Debug.Assert while returning the cached task in Release, absent evidence of a supported app-controlled route to that call. By contrast, an invalid argument supplied through a public entry point should still receive a precise runtime exception.
- Dominant language
- C#
- Stars
- 38.5k
- Forks
- 13.7k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 216
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/aspnetcore
-
area-middleware Docs
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
dotnet/aspnetcore#69942 · 3 comments ·
Maintainers usually reply within 1 day
-
area-middleware
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
dotnet/aspnetcore#69849 · 1 comment ·
Maintainers usually reply within 1 day
-
area-signalr
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
dotnet/aspnetcore#69842 · 1 comment ·
Maintainers usually reply within 1 day
-
area-minimal NativeAOT
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
dotnet/aspnetcore#69758 · 1 comment ·
Maintainers usually reply within 1 day
-
WebApplicationFactory sometimes throws ObjectDisposedException instead of the app's startup exceptionPossibly taken @Demivan claimed this 15 days ago. Openarea-mvc
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
dotnet/aspnetcore#69684 · 2 comments ·
Maintainers usually reply within 1 day
All issues in dotnet/aspnetcore
Similar issues
-
area: client bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
bug mcp misleading small
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
hazeliscoding/gil-sweep#25 ·
Maintainers usually reply within 1 day
-
Bug Platform: Android
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
getsentry/sentry-dotnet#5764 · 1 comment ·
Maintainers usually reply within 1 day