Architecture Decision Record
ADR-049: Library-Scoped ConfigureAwait(false) Policy (CA2007)
Status
Accepted (2026-07-20; measurements re-anchored 2026-08-07, 2026-08-14, 2026-08-18, 2026-08-23,
2026-08-31, 2026-09-01, 2026-09-03, 2026-09-11, 2026-09-19, 2026-10-01, 2026-10-06 and 2026-10-07).
Revised 2026-09-19: the framework site counts and the consumer-scale upper bound are re-measured, and
the occurrence-versus-line delta still turns on the same two ADC handler lines. The policy, the gate
and the exemption are unchanged.
Revised 2026-10-06: the UI exemption is restated as leaving the rule unenforced in mixed UI-package
code (those packages now call ConfigureAwait(false) 154 times), and the drift script is no longer
credited with guarding the repo-delta section.
Revised 2026-10-07: the consumer-scale figure is re-measured after ADC growth (ADC 1,544 occurrences,
2,359 combined) and the .editorconfig gate and exemption anchors are refreshed.
Context
MMCA.Common ships as NuGet packages consumed by host applications, not as an application itself.
Library code that awaits without ConfigureAwait(false) captures the caller's SynchronizationContext
and resumes on it. In ASP.NET Core hosts there is no synchronization context, so the capture is a
no-op; that is why the workspace baseline disables the ConfigureAwait analyzers everywhere
(CA2007, MA0004, RCS1090, VSTHRD111 in each repo's .editorconfig), and for the three
application repos (Store, ADC, Helpdesk) that remains the right call.
But the framework's packages do not get to choose their callers. MMCA.Common.UI.Maui (ADR-042)
runs inside MAUI, which HAS a UI synchronization context, and any future non-ASP.NET consumer
(WPF/WinForms tooling, a console host with a custom context) inherits the same exposure: a
context-capturing await inside the packages is the classic library deadlock and needless
context-hopping cost. Until now the framework relied on the ASP.NET-only assumption instead of the
standard .NET library guidance (libraries call ConfigureAwait(false); applications do not need to).
Decision
Packaged non-UI framework code awaits with ConfigureAwait(false); UI component packages and
application code do not.
- Enforcement is a build gate, not a convention. The MMCA.Common
.editorconfigrepo-delta section raisesCA2007towarningfor[Source/**.cs](a build error underTreatWarningsAsErrors), scoped back tononefor[Source/Presentation/MMCA.Common.UI*/**.cs]. Tests keep the baseline (xUnit has no synchronization context worth preserving, and test code is not shipped). - UI component packages are excluded deliberately. The exemption glob covers the whole
MMCA.Common.UI*family, which is three packaged projects, not two:MMCA.Common.UI(the Blazor component library),MMCA.Common.UI.Web(the Blazor Web host services) andMMCA.Common.UI.Maui(the MAUI capability adapters). Their component code must resume on the renderer/UI context, whereConfigureAwait(false)would be a bug, but the packages also hold code that does not (service plumbing, token storage, the server-side same-origin proxy inMMCA.Common.UI.Web/SameOriginProxy/) and callsConfigureAwait(false)on purpose. The exemption is therefore a choice to leave the rule unenforced in mixed UI-package code and decide per call site, not a claim that the call is wrong everywhere in those packages. - The application repos keep the baseline. Store, ADC and Helpdesk are ASP.NET Core hosts
(plus Blazor/MAUI heads);
CA2007/MA0004stay off in the shared analyzer baseline, per the same guidance that libraries and applications have opposite defaults. - One analyzer owns the rule.
CA2007is the enforced gate; the overlappingMA0004,RCS1090andVSTHRD111stay disabled so a violation reports once, not four times.
Rationale
- Correctness for the one consumer that already has a context. The MAUI head consumes
Infrastructure/Application/API packages through DI; a sync-over-async call anywhere in that stack
(or a consumer's
.GetAwaiter().GetResult()bridge) deadlocks only when the library captured the context.ConfigureAwait(false)removes the failure mode at the source. - Standard .NET library guidance, applied at the boundary where it holds. The rule is scoped to
exactly the code that ships in packages; it is not blanket-applied to the apps, where it would be
360+ sites of pure noise (measured across Store/ADC before this decision, and the current scale is
far past that: a raw
\bawait\bscan of*.cson 2026-10-07 counts 815 occurrences inMMCA.Store/Sourceand 1,544 inMMCA.ADC/Source, 2,359 combined, which is the upper bound on the CA2007 sites the rule would open there). - Mechanical, with the enforcement and the remediation at different levels. The build gate is the
enforced half: a new context-capturing await in packaged non-UI code fails the build, so it costs no
review effort. The remediation is a convention rather than an artifact:
dotnet format analyzers --diagnostics CA2007fixes a batch in place, but no script, CI step orCONTRIBUTING.mdentry invokes it, so it is guidance for whoever trips the gate and not automation the repo runs.
Trade-offs
- Visual noise in framework source. Every await in
Source/(except UI packages) carries.ConfigureAwait(false)(324 sites at adoption; 1,148 gated sites as of the 2026-10-06 snapshot, out of 1,302 acrossSource/once the 154 calls in the exempt UI packages are counted back in). The gate makes it uniform, so the noise is consistent rather than sporadic. - A per-repo delta in an otherwise shared analyzer baseline. The workspace keeps one
byte-identical
.editorconfigbaseline across the four repos; this policy lives in the marked repo-delta section of MMCA.Common's file (MMCA.Common/.editorconfig:821-838), so the divergence is documented. It is not mechanically guarded: the workspace drift script (Tools\Scripts\compare-analyzer-config.ps1) compares only the lines before the# REPO-SPECIFIC DELTASmarker (Tools/Scripts/compare-analyzer-config.ps1:26-28), so it guards the shared baseline (includingCA2007atnone,MMCA.Common/.editorconfig:348) and would not notice the gate at:834-835or the exemption at:837-838being changed or deleted. - UI exclusion relies on project naming. The
MMCA.Common.UI*path glob is what exempts the component packages; a renamed or relocated UI project would silently fall under the gate (the build would fail loudly on the first missingConfigureAwait, so the failure is visible, just not self-explaining).
Related
ADR-042 (the MAUI package whose synchronization context motivates the policy), ADR-027 (the same "machine-boundary hygiene as a build gate" posture applied to culture-explicit formatting via MA0076), ADR-015 (fitness-function philosophy: invariants enforced by the build, not by review).
Revision (2026-08-07)
An audit against the code. The policy did not change; three statements about it did.
- The exemption covers three packages, not the two the Decision named. The glob is
[Source/Presentation/MMCA.Common.UI*/**.cs]with severitynone(MMCA.Common/.editorconfig:831-832), sitting under the[Source/**.cs]gate at:828-829, andSource/Presentation/holds three projects whose names start withMMCA.Common.UI:MMCA.Common.UI(Source/Presentation/MMCA.Common.UI/MMCA.Common.UI.csproj),MMCA.Common.UI.Web(Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj) andMMCA.Common.UI.Maui(Source/Presentation/MMCA.Common.UI.Maui/MMCA.Common.UI.Maui.csproj). All three are packaged (MMCA.Common.UI.Webdeclares<PackageId>MMCA.Common.UI.Web</PackageId>atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj:3), so naming only two of them left a reader concluding thatMMCA.Common.UI.Webwas gated when it is not. The exclusion is right on the merits (its services run on the Blazor circuit and the SSR prerender path, for exampleServerTokenStorageServiceatMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:18), but it was undocumented. The Decision bullet now names the family and all three members. - The site counts are re-measured and dated. The "324 sites at adoption" figure is a
2026-07-20 snapshot and stays as history. Measured on 2026-08-07,
MMCA.Common/Source/**/*.csholds 719ConfigureAwait(false)occurrences across 147 files, of which 90 sit inside the exempt packages (MMCA.Common.UI47,MMCA.Common.UI.Maui41,MMCA.Common.UI.Web2), leaving 629 under the gate. The consumer-scale figure in the Rationale is likewise re-anchored: a rawawaitscan gives 593 occurrences inMMCA.Store/Source/**/*.cs(112 files) and 1,175 inMMCA.ADC/Source/**/*.cs(217 files). Rawawaitovercounts CA2007 sites (it catchesawait using,await foreachand awaits the analyzer would not flag), so those two numbers are an upper bound, which is the same direction the original "360+" phrasing pointed. - "Mechanical and self-maintaining" conflated an enforced gate with an unenforced habit. The
gate is real and enforced:
warningunderTreatWarningsAsErrors(trueatMMCA.Common/Directory.Build.props:7, withCodeAnalysisTreatWarningsAsErrorsat:13and CA2007 absent from everyNoWarnlist) is a build error. The remediation command is not backed by any repo artifact: nothing in the repo invokesdotnet format analyzers --diagnostics CA2007, so the Rationale now presents it as guidance for a developer who trips the gate, not as tooling the build or CI runs.
Revision (2026-08-14)
A re-measurement only. The policy, the gate and the exemption are unchanged; the counts the document quotes were a week old and had moved by roughly 9%.
- Framework site counts, measured 2026-08-14.
MMCA.Common/Source/**/*.csnow holds 786ConfigureAwait(false)occurrences across 158 files, of which 93 sit inside the exempt UI packages (MMCA.Common.UI49 across 15 files,MMCA.Common.UI.Maui42 across 16 files,MMCA.Common.UI.Web2 in 1 file), leaving 693 under the gate. The 2026-08-07 figures the previous revision recorded (719 / 147 files, 90 exempt, 629 gated) stay in that revision as the history of that measurement; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-08-14. A raw
\bawait\bscan gives 616 occurrences across 118 files inMMCA.Store/Source/**/*.csand 1,380 across 259 files inMMCA.ADC/Source/**/*.cs, 1,996 combined, up from 593 / 1,175 on 2026-08-07 (ADC accounts for most of the growth, which is the conference feature work shipped that week). Rawawaitstill overcounts CA2007 sites, so this is an upper bound and it points the same way the original "360+" phrasing did: only harder. The Rationale now names the pattern (\bawait\b) so the figure is reproducible rather than method-dependent. - Everything else re-verified and unchanged. The
[Source/**.cs]gate atwarning(MMCA.Common/.editorconfig:828-829) with the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption atnone(:831-832), the three packagedMMCA.Common.UI*projects (including<PackageId>MMCA.Common.UI.Web</PackageId>atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj:3andServerTokenStorageServiceatMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:18),TreatWarningsAsErrors(MMCA.Common/Directory.Build.props:7) withCodeAnalysisTreatWarningsAsErrorsat:13and CA2007 in noNoWarnlist, and the absence of any repo artifact invokingdotnet format analyzers --diagnostics CA2007all still hold as written.
Revision (2026-08-18)
A re-measurement only, in the same terms as the 2026-08-14 pass. The policy, the gate and the exemption are unchanged; two of the three counted figures moved.
- Framework site counts, measured 2026-08-18.
MMCA.Common/Source/**/*.csnow holds 811ConfigureAwait(false)occurrences across 168 files, of which 93 sit inside the exempt UI packages (MMCA.Common.UI49 across 15 files,MMCA.Common.UI.Maui42 across 16 files,MMCA.Common.UI.Web2 in 1 file), leaving 718 under the gate. The exempt split is unchanged from 2026-08-14, so all 25 new occurrences (and all 10 new files) landed in gated code. The 2026-08-14 figures (786 / 158 files, 93 exempt, 693 gated) and the 2026-08-07 figures (719 / 147 files, 90 exempt, 629 gated) stay in their own revisions as the history of those measurements; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-08-18. A raw
\bawait\bscan gives 616 occurrences across 118 files inMMCA.Store/Source/**/*.cs(identical to 2026-08-14: Store did not move) and 1,386 across 261 files inMMCA.ADC/Source/**/*.cs(up from 1,380 across 259 files), 2,002 combined. ADC again accounts for all of the growth. Rawawaitstill overcounts CA2007 sites, so this stays an upper bound and it points the same way the original "360+" phrasing did. - The gate, the exemption and the enforcement are re-verified as written. The
[Source/**.cs]gate atwarning(MMCA.Common/.editorconfig:828-829), the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption atnone(:831-832), the three packagedMMCA.Common.UI*projects, andTreatWarningsAsErrors(MMCA.Common/Directory.Build.props:7) withCodeAnalysisTreatWarningsAsErrorsat:13, all still hold. The statement that no repo artifact invokesdotnet format analyzers --diagnostics CA2007was not re-searched in this pass; it carries forward from the 2026-08-07 revision that established it.
Revision (2026-08-23)
A re-measurement only, in the same terms as the 2026-08-18 pass. The policy, the gate and the exemption are unchanged; both counted figures moved.
- Framework site counts, measured 2026-08-23.
MMCA.Common/Source/**/*.csnow holds 860ConfigureAwait(false)occurrences across 176 files, of which 93 sit inside the exempt UI packages (MMCA.Common.UI49 across 15 files,MMCA.Common.UI.Maui42 across 16 files,MMCA.Common.UI.Web2 in 1 file), leaving 767 under the gate. The exempt split is unchanged from both 2026-08-14 and 2026-08-18, so all 49 new occurrences (and all 8 new files) landed in gated code, which is what a working gate looks like: every await added to packaged non-UI code in the last five days carries the call. The 2026-08-18 figures (811 / 168 files, 93 exempt, 718 gated), the 2026-08-14 figures (786 / 158 files, 93 exempt, 693 gated) and the 2026-08-07 figures (719 / 147 files, 90 exempt, 629 gated) stay in their own revisions as the history of those measurements; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-08-23. A raw
\bawait\bscan gives 617 occurrences across 119 files inMMCA.Store/Source/**/*.cs(up from 616 across 118 files: Store is effectively flat) and 1,462 across 272 files inMMCA.ADC/Source/**/*.cs(up from 1,386 across 261 files), 2,079 combined. ADC again accounts for essentially all of the growth. Rawawaitstill overcounts CA2007 sites, so this stays an upper bound and it points the same way the original "360+" phrasing did. - The gate, the exemption and the enforcement are re-verified as written. The
[Source/**.cs]gate atwarning(MMCA.Common/.editorconfig:828-829), the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption atnone(:831-832), the three packagedMMCA.Common.UI*projects (including<PackageId>MMCA.Common.UI.Web</PackageId>atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj:3andServerTokenStorageServiceatMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:18), andTreatWarningsAsErrors(MMCA.Common/Directory.Build.props:7) withCodeAnalysisTreatWarningsAsErrorsat:13and CA2007 in noNoWarnlist, all still hold. The statement that no repo artifact invokesdotnet format analyzers --diagnostics CA2007was re-checked this pass against MMCA.Common's workflow files only (no match); the broader claim still rests on the 2026-08-07 revision that established it.
Revision (2026-08-31)
A re-measurement plus a correction to two file anchors. The policy, the gate and the exemption are
unchanged. Every counted figure moved, two of the surrounding narratives did not survive the
re-measurement, and the .editorconfig line anchors this document has cited since 2026-08-07 shifted.
- Framework site counts, measured 2026-08-31.
MMCA.Common/Source/**/*.csnow holds 1,012ConfigureAwait(false)occurrences across 197 files, of which 102 sit inside the exempt UI packages across 35 files (MMCA.Common.UI49 across 15 files,MMCA.Common.UI.Maui51 across 19 files,MMCA.Common.UI.Web2 in 1 file), leaving 910 under the gate across 162 files. The exempt split is no longer unchanged. It held flat at 93 through 2026-08-14, 2026-08-18 and 2026-08-23, and the earlier revisions read that as evidence that every new await landed in gated code;MMCA.Common.UI.Mauihas since grown from 42 across 16 files to 51 across 19, so the "all new occurrences landed in gated code" reading does not carry forward to this window. The gate claim it was standing in for is unaffected: the exempt projects are exempt by design, and growth there is Blazor/MAUI components resuming on the renderer context, exactly what the exclusion is for. The 2026-08-23 figures (860 / 176 files, 93 exempt, 767 gated), the 2026-08-18 figures (811 / 168 files, 93 exempt, 718 gated), the 2026-08-14 figures (786 / 158 files, 93 exempt, 693 gated) and the 2026-08-07 figures (719 / 147 files, 90 exempt, 629 gated) stay in their own revisions as the history of those measurements; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-08-31, and it fell. A raw
\bawait\bscan gives 569 occurrences across 102 files inMMCA.Store/Source/**/*.cs(down from 617 across 119 files) and 1,291 across 246 files inMMCA.ADC/Source/**/*.cs(down from 1,462 across 272 files), 1,860 combined, down from 2,079. Both consumers shrank in this window, which is the first time either has: the work that landed in it collapses duplicated code paths and moves module CRUD onto the framework's generic write-side handlers, so it deletes application code rather than adding it. The "the scale has only grown" and "ADC accounts for essentially all of the growth" framings are retired. The figure is a snapshot of how much noise the rule would open in the apps, not a trend line, and the Rationale now reads that way. Rawawaitstill overcounts CA2007 sites (it catchesawait using,await foreachand awaits the analyzer would not flag), so this stays an upper bound, and at 1,860 it points the same way the original "360+" phrasing did. - The two
.editorconfiganchors moved four lines down. The gate header[Source/**.cs]is atMMCA.Common/.editorconfig:832withdotnet_diagnostic.CA2007.severity = warningat:833, and the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption header is at:835withdotnet_diagnostic.CA2007.severity = noneat:836. The shift is not a policy change: a comment block now occupies:827-831and states the rationale (packaged libraries must not capture the caller's context, UI packages excluded, apps keep the baseline) in the file itself. The:828-829and:831-832citations in the 2026-08-07, 2026-08-14, 2026-08-18 and 2026-08-23 revisions were correct at those dates and are superseded by these. - Everything else re-verified as written. CA2007 appears in exactly three places in
MMCA.Common/.editorconfig: the shared-baselinenoneat:348, the gatewarningat:833and the UI exemptionnoneat:836. NoTests-scoped override exists, so test code inherits the baselinenone, as the Decision says. The other three repos keep the baseline untouched (CA2007at:348,MA0004at:536,RCS1090at:635,VSTHRD111at:712, allnoneinMMCA.ADC/.editorconfig,MMCA.Store/.editorconfigandMMCA.Helpdesk/.editorconfigalike), and the marked delta section that carries this policy namesTools\Scripts\compare-analyzer-config.ps1as its verifier atMMCA.Common/.editorconfig:821-824. The three packagedMMCA.Common.UI*projects still stand (including<PackageId>MMCA.Common.UI.Web</PackageId>atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj:3andServerTokenStorageServiceatMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:18), as does the enforcement:TreatWarningsAsErrorsatMMCA.Common/Directory.Build.props:7,CodeAnalysisTreatWarningsAsErrorsat:13, and CA2007 absent from all threeNoWarnlists (:27,:32,:38). - The remediation command still has no repo artifact, now checked workspace-wide. The
2026-08-23 pass could only re-check MMCA.Common's workflow files and left the broader claim
resting on 2026-08-07; this pass searched the whole workspace. Every
dotnet format analyzersoccurrence outside this ADR targets the using-ordering rules SA1210/SA1211 (for exampleWebsite/docs-src/guides/common-GETTING-STARTED.md:156andMMCA.Helpdesk/build/templates/stage.ps1:1093); nothing anywhere invokes it with--diagnostics CA2007. The Rationale's framing of the command as guidance for whoever trips the gate, not automation the repo runs, is confirmed without a hedge.
Revision (2026-09-01)
A re-measurement only, in the same terms as the 2026-08-31 pass. The policy, the gate and the
exemption are unchanged, the .editorconfig anchors that shifted last pass are still where that
pass put them, and both counted figures moved by about one percent.
- Framework site counts, measured 2026-09-01.
MMCA.Common/Source/**/*.csnow holds 1,024ConfigureAwait(false)occurrences across 199 files, of which 102 sit inside the exempt UI packages across 35 files (MMCA.Common.UI49 across 15 files,MMCA.Common.UI.Maui51 across 19 files,MMCA.Common.UI.Web2 in 1 file), leaving 922 under the gate across 164 files. The exempt split is identical to 2026-08-31, so all 12 new occurrences and both new files landed in gated code. That is one observation over a one-day window, not the multi-day flat stretch the pre-2026-08-31 revisions over-read into a rule, and it is recorded as such. The 2026-08-31 figures (1,012 / 197 files, 102 exempt, 910 gated), the 2026-08-23 figures (860 / 176 files, 93 exempt, 767 gated), the 2026-08-18 figures (811 / 168 files, 93 exempt, 718 gated), the 2026-08-14 figures (786 / 158 files, 93 exempt, 693 gated) and the 2026-08-07 figures (719 / 147 files, 90 exempt, 629 gated) stay in their own revisions as the history of those measurements; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-09-01. A raw
\bawait\bscan gives 570 occurrences across 105 files inMMCA.Store/Source/**/*.cs(up from 569 across 102 files) and 1,299 across 256 files inMMCA.ADC/Source/**/*.cs(up from 1,291 across 246 files), 1,869 combined, up from 1,860. The one-window shrink recorded on 2026-08-31 did not continue, and both consumers are close to flat; consistent with that revision, the figure stays a snapshot of how much noise the rule would open in the apps rather than a trend line. One methodology note, in the spirit of the 2026-08-14 pass that named the pattern so the figure would be reproducible: these are occurrence counts, not counts of matching lines, and the two differ by exactly one, in ADC, whereSessionScoringProcessorputs two awaits on a single line (await using var claim = await ...atMMCA.ADC/Source/Modules/Conference/MMCA.ADC.Conference.Infrastructure/Services/SessionScoringProcessor.cs:177); a per-line scan reports 1,298 for ADC and 1,868 combined. Rawawaitstill overcounts CA2007 sites either way (it catchesawait using,await foreachand awaits the analyzer would not flag), so this remains an upper bound. - The gate, the exemption and the enforcement are re-verified as written. CA2007 appears in
exactly three places in
MMCA.Common/.editorconfig: the shared-baselinenoneat:348, the[Source/**.cs]gate header at:832withdotnet_diagnostic.CA2007.severity = warningat:833, and the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption header at:835withdotnet_diagnostic.CA2007.severity = noneat:836. The rationale comment block still occupies:827-831and the delta marker namingTools\Scripts\compare-analyzer-config.ps1still sits at:821-824. NoTests-scoped override exists, so test code inherits the baselinenone. The other three repos keep the baseline untouched (CA2007at:348,MA0004at:536,RCS1090at:635,VSTHRD111at:712, allnoneinMMCA.ADC/.editorconfig,MMCA.Store/.editorconfigandMMCA.Helpdesk/.editorconfigalike). The three packagedMMCA.Common.UI*projects still stand (including<PackageId>MMCA.Common.UI.Web</PackageId>atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj:3andServerTokenStorageServiceatMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:18), as does the enforcement:TreatWarningsAsErrorsatMMCA.Common/Directory.Build.props:7,CodeAnalysisTreatWarningsAsErrorsat:13, and CA2007 absent from all threeNoWarnlists (:27,:32,:38). - The remediation command still has no repo artifact. The workspace-wide search the 2026-08-31
pass introduced was re-run: every
dotnet format analyzersoccurrence outside this ADR still targets the using-ordering rules SA1210/SA1211 (for exampleWebsite/docs-src/guides/common-GETTING-STARTED.md:156andMMCA.Helpdesk/build/templates/stage.ps1:1093), and nothing invokes it with--diagnostics CA2007.
Revision (2026-09-03)
A re-measurement plus one anchor correction, in the same terms as the 2026-09-01 pass. The policy,
the gate and the exemption are unchanged, the .editorconfig anchors are still where the 2026-08-31
pass put them, and the counted figures moved by well under one percent.
- Framework site counts, measured 2026-09-03.
MMCA.Common/Source/**/*.csnow holds 1,030ConfigureAwait(false)occurrences across 200 files, of which 102 sit inside the exempt UI packages across 35 files (MMCA.Common.UI49 across 15 files,MMCA.Common.UI.Maui51 across 19 files,MMCA.Common.UI.Web2 in 1 file), leaving 928 under the gate across 165 files. The exempt split is identical to 2026-08-31 and 2026-09-01, so all 6 new occurrences and the single new file landed in gated code; as the previous pass recorded, that is one observation over a two-day window and not a rule. No line inSource/carries twoConfigureAwait(false)calls, so the occurrence count and the matching-line count are the same number here. The 2026-09-01 figures (1,024 / 199 files, 102 exempt, 922 gated), the 2026-08-31 figures (1,012 / 197 files, 102 exempt, 910 gated), the 2026-08-23 figures (860 / 176 files, 93 exempt, 767 gated), the 2026-08-18 figures (811 / 168 files, 93 exempt, 718 gated), the 2026-08-14 figures (786 / 158 files, 93 exempt, 693 gated) and the 2026-08-07 figures (719 / 147 files, 90 exempt, 629 gated) stay in their own revisions as the history of those measurements; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-09-03. A raw
\bawait\bscan gives 570 occurrences across 105 files inMMCA.Store/Source/**/*.cs(identical to 2026-09-01: Store did not move) and 1,298 across 255 files inMMCA.ADC/Source/**/*.cs(down from 1,299 across 256 files), 1,868 combined. Consistent with 2026-08-31, the figure stays a snapshot of how much noise the rule would open in the apps rather than a trend line. The occurrence-versus-line note from 2026-09-01 still holds and still turns on one line:SessionScoringProcessorputs two awaits on a single line (await using var claim = await ...), so a per-line scan reports 1,297 for ADC and 1,867 combined. That file moved with the folder reorganization and now sits atMMCA.ADC/Source/Modules/Conference/MMCA.ADC.Conference.Infrastructure/Sessions/Scoring/SessionScoringProcessor.cs:177; the.../Conference.Infrastructure/Services/SessionScoringProcessor.cs:177path the 2026-09-01 revision cites was correct at that date and is superseded by this one. Rawawaitstill overcounts CA2007 sites (it catchesawait using,await foreachand awaits the analyzer would not flag), so this remains an upper bound. - The gate, the exemption and the enforcement are re-verified as written. CA2007 appears in
exactly three places in
MMCA.Common/.editorconfig: the shared-baselinenoneat:348, the[Source/**.cs]gate header at:832withdotnet_diagnostic.CA2007.severity = warningat:833, and the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption header at:835withdotnet_diagnostic.CA2007.severity = noneat:836. The rationale comment block still occupies:827-831and the delta marker namingTools\Scripts\compare-analyzer-config.ps1still sits at:821-824. NoTests-scoped override exists, so test code inherits the baselinenone. The other three repos keep the baseline untouched (CA2007at:348,MA0004at:536,RCS1090at:635,VSTHRD111at:712, allnoneinMMCA.ADC/.editorconfig,MMCA.Store/.editorconfigandMMCA.Helpdesk/.editorconfigalike). The three packagedMMCA.Common.UI*projects still stand (including<PackageId>MMCA.Common.UI.Web</PackageId>atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj:3), as does the enforcement:TreatWarningsAsErrorsatMMCA.Common/Directory.Build.props:7,CodeAnalysisTreatWarningsAsErrorsat:13, and CA2007 absent from all threeNoWarnlists (:27,:32,:38). - The
ServerTokenStorageServiceanchor was off by one and is corrected wherever it appears. The declarationpublic sealed class ServerTokenStorageService(sits atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:18; line 17 is the closing/// </summary>tag of its doc comment. The:17citation carried forward unchanged from the 2026-08-07 revision through every pass since, and now reads:18in all of them. - The remediation command still has no repo artifact. The workspace-wide search was re-run:
every
dotnet format analyzersoccurrence outside this ADR targets the using-ordering rules SA1210/SA1211 (for exampleWebsite/docs-src/guides/common-GETTING-STARTED.md:156andMMCA.Helpdesk/build/templates/stage.ps1:1093), and nothing invokes it with--diagnostics CA2007.
Revision (2026-09-11)
A re-measurement plus one anchor correction, in the same terms as the 2026-09-03 pass. The policy,
the gate and the exemption are unchanged and the .editorconfig anchors are still where the
2026-08-31 pass put them, but every counted figure moved well past the sub-one-percent drift the
last two passes recorded.
- Framework site counts, measured 2026-09-11.
MMCA.Common/Source/**/*.csnow holds 1,169ConfigureAwait(false)occurrences across 231 files, of which 110 sit inside the exempt UI packages across 37 files (MMCA.Common.UI56 across 17 files,MMCA.Common.UI.Maui52 across 19 files,MMCA.Common.UI.Web2 in 1 file), leaving 1,059 under the gate across 194 files. The exempt split, identical from 2026-08-31 through 2026-09-03, moved this time:MMCA.Common.UIgained 7 occurrences and 2 files andMMCA.Common.UI.Mauigained 1 occurrence, so growth is no longer confined to gated code. No line inSource/carries twoConfigureAwait(false)calls, so the occurrence count and the matching-line count are the same number here (1,169 both ways). The 2026-09-03 figures (1,030 / 200 files, 102 exempt, 928 gated) and every earlier set stay in their own revisions as the history of those measurements; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-09-11. A raw
\bawait\bscan gives 781 occurrences across 144 files inMMCA.Store/Source/**/*.cs(up from 570 across 105 files) and 1,359 across 264 files inMMCA.ADC/Source/**/*.cs(up from 1,298 across 255 files), 2,140 combined. The Store move is the largest this ADR has recorded. The gRPC service hosts underMMCA.Store/Source/Services(MMCA.Store.Catalog.Service,MMCA.Store.Identity.Service,MMCA.Store.Sales.Serviceand theirContractsprojects) account for only 24 of those occurrences across 11 files, so the bulk of the growth sits in existing module code. Consistent with every pass since 2026-08-31, the figure stays a snapshot of how much noise the rule would open in the apps rather than a trend line. Rawawaitstill overcounts CA2007 sites (it catchesawait using,await foreachand awaits the analyzer would not flag), so this remains an upper bound. - The occurrence-versus-line delta now turns on two lines, and
SessionScoringProcessoris gone. The file cited by the 2026-09-01 and 2026-09-03 revisions (.../Conference.Infrastructure/Sessions/Scoring/SessionScoringProcessor.cs:177) no longer exists anywhere underMMCA.ADC/Source; the work moved into Application-layer handlers. Theawait using var claim = await ...pattern now sits on exactly two lines,MMCA.ADC/Source/Modules/Conference/MMCA.ADC.Conference.Application/Sessions/UseCases/DecisionSupport/ScoreEventSessions/ScoreEventSessionsInternalCommandHandler.cs:75andMMCA.ADC/Source/Modules/Engagement/MMCA.ADC.Engagement.Application/SessionQuestions/UseCases/Submit/SubmitQuestionHandler.cs:147, so a per-line scan reports 1,357 for ADC and 2,138 combined.MMCA.Store/Sourcecarries no double-await line, so its 781 occurrences and 781 matching lines are the same number. - The gate, the exemption and the enforcement are re-verified as written. CA2007 appears in
exactly three places in
MMCA.Common/.editorconfig: the shared-baselinenoneat:348, the[Source/**.cs]gate header at:832withdotnet_diagnostic.CA2007.severity = warningat:833, and the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption header at:835withdotnet_diagnostic.CA2007.severity = noneat:836. The rationale comment block still occupies:827-831and the delta marker namingTools\Scripts\compare-analyzer-config.ps1still sits at:821-824. NoTests-scoped override exists, so test code inherits the baselinenone. The packagedMMCA.Common.UI*projects still stand (including the<PackageId>MMCA.Common.UI.Web</PackageId>atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/MMCA.Common.UI.Web.csproj:3), as does the enforcement:TreatWarningsAsErrorsatMMCA.Common/Directory.Build.props:7,CodeAnalysisTreatWarningsAsErrorsat:13, and CA2007 absent from all threeNoWarnlists (:27,:32,:38). - The remediation command still has no repo artifact, and its two example anchors have shifted.
A workspace-wide search finds
dotnet format analyzers ... --diagnostics CA2007nowhere outside this ADR. Every otherdotnet format analyzersoccurrence targets the using-ordering rules SA1210/SA1211, and the two examples earlier revisions cite now sit atWebsite/docs-src/guides/common-GETTING-STARTED.md:162andMMCA.Helpdesk/build/templates/stage.ps1:1235; the:156and:1093citations in the revisions above were correct at their dates and are superseded by these.
Revision (2026-09-19)
A re-measurement in the same terms as the 2026-09-11 pass, covering the two counted figures that move (framework sites and the consumer-scale upper bound) and the occurrence-versus-line delta that depends on them. The policy, the gate and the exemption are unchanged.
- Framework site counts, measured 2026-09-19.
MMCA.Common/Source/**/*.csnow holds 1,186ConfigureAwait(false)occurrences across 232 files, of which 110 sit inside the exempt UI packages across 37 files (MMCA.Common.UI56 across 17 files,MMCA.Common.UI.Maui52 across 19 files,MMCA.Common.UI.Web2 in 1 file), leaving 1,076 under the gate across 195 files. The exempt split is identical to the 2026-09-11 split, so all 17 new occurrences and the one new file are gated code and growth is back inside the gate. No line inSource/carries twoConfigureAwait(false)calls, so the occurrence count and the matching-line count are still the same number (1,186 both ways). The 2026-09-11 figures (1,169 / 231 files, 110 exempt, 1,059 gated) and every earlier set stay in their own revisions as the history of those measurements; the Trade-offs entry now carries today's numbers. "324 sites at adoption" remains the 2026-07-20 snapshot and is unchanged. - Consumer-scale upper bound, measured 2026-09-19. A raw
\bawait\bscan gives 779 occurrences across 143 files inMMCA.Store/Source/**/*.cs(down from 781 across 144 files) and 1,453 across 281 files inMMCA.ADC/Source/**/*.cs(up from 1,359 across 264 files), 2,232 combined. ADC carries all of the growth this pass; Store gave back two occurrences and one file after the large move the 2026-09-11 pass recorded. The scan is scoped to*.cs, as this figure has been since adoption:.razorfiles would add 9 occurrences across 2 files in Store and 27 across 10 files in ADC and are deliberately outside it. Rawawaitstill overcounts CA2007 sites (it catchesawait using,await foreachand awaits the analyzer would not flag), so this remains an upper bound and a snapshot of how much noise the rule would open in the apps rather than a trend line. - The occurrence-versus-line delta still turns on the same two lines. The
await using var claim = await ...pattern sits on exactly two lines underMMCA.ADC/Source,MMCA.ADC/Source/Modules/Conference/MMCA.ADC.Conference.Application/Sessions/UseCases/DecisionSupport/ScoreEventSessions/ScoreEventSessionsInternalCommandHandler.cs:75andMMCA.ADC/Source/Modules/Engagement/MMCA.ADC.Engagement.Application/SessionQuestions/UseCases/Submit/SubmitQuestionHandler.cs:147, both unmoved from the 2026-09-11 pass, so a per-line scan reports 1,451 for ADC and 2,230 combined.MMCA.Store/Sourcecarries no double-await line, so its 779 occurrences and 779 matching lines are the same number.
Revision (2026-10-01)
A count and anchor refresh only. No decision or rationale changed: the policy, the gate
(MMCA.Common/.editorconfig:833) and the UI exemption (:836) stand as written. The Rationale and
Trade-offs figures are refreshed to the measurements below; the earlier revisions keep theirs as
history. Framework counts are taken at MMCA.Common main eeb87e6c; Store counts at
origin/main 53acbe40.
- Framework site counts.
MMCA.Common/Source/**/*.csholds 1,234ConfigureAwait(false)occurrences across 242 files (1,234 matching lines, so no line carries two), of which 119 sit inside the exempt UI packages across 39 files (MMCA.Common.UI63 across 19 files,MMCA.Common.UI.Maui54 across 19 files,MMCA.Common.UI.Web2 in 1 file), leaving 1,115 under the gate across 203 files. The exempt split moved (MMCA.Common.UIgained 7 occurrences and 2 files,MMCA.Common.UI.Maui2 occurrences), so growth is not confined to gated code this window. - Consumer-scale upper bound. A raw
\bawait\bscan gives 793 occurrences across 152 files inMMCA.Store/Source/**/*.csand 1,490 across 291 files inMMCA.ADC/Source/**/*.cs, 2,283 combined..razorfiles, still outside the scan, would add 9 occurrences across 2 files in Store and 23 across 9 files in ADC. - The occurrence-versus-line delta still turns on two lines. The
await using var claim = await ...pattern sits atMMCA.ADC/Source/Modules/Conference/MMCA.ADC.Conference.Application/Sessions/UseCases/DecisionSupport/ScoreEventSessions/ScoreEventSessionsInternalCommandHandler.cs:78(previously:75) andMMCA.ADC/Source/Modules/Engagement/MMCA.ADC.Engagement.Application/SessionQuestions/UseCases/Submit/SubmitQuestionHandler.cs:147, so a per-line scan reports 1,488 for ADC and 2,281 combined. Store carries no double-await line (793 both ways). - The remediation examples moved. The two
dotnet format analyzersexamples that target SA1210/SA1211 now sit atWebsite/docs-src/guides/common-GETTING-STARTED.md:186andMMCA.Helpdesk/build/templates/stage.ps1:1236, superseding the:162and:1235citations in the 2026-09-11 revision.
Revision (2026-10-06)
A content correction plus a count and anchor refresh. The gate (MMCA.Common/.editorconfig:832-833)
and the UI exemption (:835-836) are unchanged; two statements about them were not accurate.
- The UI exemption is not "the call would be a bug" everywhere. The exempt packages now hold
154
ConfigureAwait(false)calls across 43 files:MMCA.Common.UI76 across 20 files,MMCA.Common.UI.Maui54 across 19,MMCA.Common.UI.Web24 across 4 (SameOriginProxy/SameOriginApiProxyEndpoint.cs15,SameOriginProxy/SameOriginProxyTransformer.cs5,Services/ServerTokenStorageService.cs3,SameOriginProxy/SessionHandoffEndpoints.cs1). EvenServerTokenStorageService, which earlier revisions cite as the exemplar of circuit-bound code (its declaration is now atMMCA.Common/Source/Presentation/MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:30), calls it at:99,:155and:161. The Decision now describes the exemption as leaving the rule unenforced in mixed UI-package code, and the 2026-08-31 reading that exempt-package growth "is Blazor/MAUI components resuming on the renderer context" does not hold. - The drift script does not guard this policy.
Tools/Scripts/compare-analyzer-config.ps1diffs only the lines before the# REPO-SPECIFIC DELTASmarker (:26-28), so it guards the shared baseline, not the repo-delta section holding the gate. The Trade-offs entry now says so. The marker comment atMMCA.Common/.editorconfig:821-824describes the script accurately. - Framework site counts.
MMCA.Common/Source/**/*.csholds 1,302ConfigureAwait(false)occurrences across 249 files (no line carries two), 154 of them exempt across 43 files, leaving 1,148 under the gate across 206 files. - Consumer-scale upper bound. A raw
\bawait\bscan of*.csgives 815 occurrences across 154 files inMMCA.Store/Sourceand 1,540 across 301 files inMMCA.ADC/Source, 2,355 combined. The twoawait using var claim = await ...lines are still the only double-await lines,ScoreEventSessionsInternalCommandHandler.cs:83(previously:78) andSubmitQuestionHandler.cs:147, so a per-line scan reports 1,538 for ADC and 2,353 combined; Store has none. - Anchors re-verified against current source.
TreatWarningsAsErrorsatMMCA.Common/Directory.Build.props:7andCodeAnalysisTreatWarningsAsErrorsat:13hold, and the threeNoWarnlists now sit at:30,:35and:41, none naming CA2007. The SA1210/SA1211 remediation examples sit atWebsite/docs-src/guides/common-GETTING-STARTED.md:190andMMCA.Helpdesk/build/templates/stage.ps1:1236; nothing invokes--diagnostics CA2007.
Revision (2026-10-07)
Re-verified against current source. The policy, the gate and the UI exemption are unchanged, and the
framework site counts in the Trade-offs entry were not re-measured this pass. What moved is the
consumer-scale figure, which grew with new ADC code, and the .editorconfig anchors.
- The consumer-scale figure is re-measured. ADC grew by four
awaitlines since the 2026-10-06 pass (1,538 matching lines then, 1,542 now, still across 301 files), so its occurrence count moves from 1,540 to 1,544. A per-file line count understates occurrences only where a line holds two tokens:SubmitQuestionHandler.csreports 9 lines but holds 10awaittokens. Measured 2026-10-07, a raw\bawait\bscan of*.csgives 1,542 matching lines across 301 files inMMCA.ADC/Sourceand 815 across 154 files inMMCA.Store/Source. The only double-await lines are still the twoawait using var claim = await distributedLocklines,MMCA.ADC/Source/Modules/Conference/MMCA.ADC.Conference.Application/Sessions/UseCases/DecisionSupport/ScoreEventSessions/ScoreEventSessionsInternalCommandHandler.cs:83andMMCA.ADC/Source/Modules/Engagement/MMCA.ADC.Engagement.Application/SessionQuestions/UseCases/Submit/SubmitQuestionHandler.cs:147, so ADC holds 1,544 occurrences and the combined figure is 2,359 occurrences (2,357 matching lines). Store has no double-await line, so its 815 is the same both ways. The Rationale now carries the occurrence figures. Rawawaitstill overcounts CA2007 sites, so this remains an upper bound. - Anchors re-verified against current source: the
MMCA.Common/.editorconfigrationale comment block grew to:827-833, moving the[Source/**.cs]gate to:834-835and the[Source/Presentation/MMCA.Common.UI*/**.cs]exemption to:837-838(previously:832-833and:835-836), so the repo-delta section now spans:821-838; the shared-baselineCA2007nonestays at:348and the marker namingTools\Scripts\compare-analyzer-config.ps1at:821-824.Tools/Scripts/compare-analyzer-config.ps1:26-28still slices only the lines before the marker.TreatWarningsAsErrorsatMMCA.Common/Directory.Build.props:7,CodeAnalysisTreatWarningsAsErrorsat:13, and the threeNoWarnlists at:30,:35and:41(none naming CA2007) hold.