Architecture governance

MMCA.ADC — Architecture Scorecard

Canonical, version-controlled scorecard for this repo — the single source of truth for MMCA.ADC's architecture scores (replaces the former single-axis snapshot; see git history). Scored against the rubric at ../MMCA.Common/ArchitectureEvaluationCriteria.md; framework-wide facts in ../MMCA.Common/FACTS.md. Remediation lives in RemediationBacklog.md (the single ledger — the former TECHDEBT.md tactical TD-NN register was folded in 2026-06-26); the cross-repo comparison in ../Docs/Architecture/CrossRepoComparison.md.

Rubric: ../MMCA.Common/ArchitectureEvaluationCriteria.md • Date: 2026-07-23 • Two axes per category: Maturity (0-4, process/governance) and Implementation (0-10, substance/execution). Indices computed deterministically from the scores below. Verified against current source (HEAD 160f59f5, clean tree); framework dependency pinned at MMCA.Common. v1.123.0* (all 15 packages, lockstep, Directory.Packages.props; the canonical ADR set is 001-051, indexed in ../adr/README.md; framework-wide facts in ../MMCA.Common/FACTS.md). What moved this cycle (twenty-third-cycle full 34-category re-score, 2026-07-23, pin v1.123.0): no score moves; all 34 categories re-confirmed at their prior scores from evidence read this run (32 CONFIRMED first-pass, 2 adversarially adjudicated). The two adjudications were proposed maturity lifts, both rejected as verified non-moves: §12 holds M3/I8 (the k6 capacity tier still executes monthly cron + workflow_dispatch out of band, load-test.yml:8-18, and deploy's load-freshness is a recency check on the last successful run with no per-deploy k6 cost, deploy.yml:548-551; Notification stays pinned maxReplicas: 1, main.bicep:1424) and §21 holds M3/I8 (the recorded, dated manual screen-reader pass, the sole maturity-4 lever, is still the empty placeholder at adc-ACCESSIBILITY-SCREENREADER-PASS.md:62; the 18-page chromium axe/E2E deploy gate re-confirmed active via e2e-gate in deploy.needs, deploy.yml:791). §22 and §33 re-confirmed at M3/I8, so the below-maturity-4 set stays §12/§21/§22/§33; every implementation score is 8 or higher. The v1.122.0/v1.123.0 lockstep sweeps (filter DSL + cache observability; the IIntegrationEventPublisher removal with callers moved to IEventBus directly) moved no score. Indices unchanged: Maturity 97.2% (311/320) / Implementation 85.9% (687/800). The twenty-second-cycle record follows for provenance: (twenty-second cycle, 2026-07-21, pin v1.121.0): two scores move, both down, both on drift verified in current source and neither a code-quality regression. §22 Responsive & Cross-Browser Maturity 4→3. The twenty-first cycle's M4 rested on the deploy-gating e2e-gate passing all three engines; the 2026-07-18 Actions-minute reduction cut that gate to chromium only (deploy.yml:488 browsers: '["chromium"]', with the cost rationale recorded in the job comment at :478-480), so firefox and webkit now run only on the weeknight schedule (e2e.yml:39), where they are continue-on-error (e2e.yml:119). Cross-engine verification is therefore nightly-advisory, which is the rubric's Consistent (3), not Optimized (4); the responsive substance is untouched, so Implementation holds at 8. This is the scoring consequence of a deliberate cost choice, recorded as such in the backlog's Deliberate/accepted section. §18 UI Architecture & Components Implementation 9→8. Maturity 4 re-confirmed independently (UIArchitectureConventionTests.cs is a real sealed subclass of the shared base, the arch-test project is in MMCA.ADC.CI.slnf:58, and that filter is built and tested by the required build-and-test check at deploy.yml:181), but the exemplary-band 9 no longer holds: the largest code-behind in the repo, Engagement.UI/Pages/HappeningNow/HappeningNow.razor.cs, sits at exactly 400 lines against the enforced MaxCodeBehindLines => 400 cap (MMCA.Common.Testing.Architecture/Bases/UIArchitectureConventionTestsBase.cs:22), with six more files in the 360-379 band, so the next added method fails the gate rather than being caught in review. That is a real, isolated, citable gap (rubric Strong 7-8), and the prior 9 also rested partly on a broken citation (MobileInfiniteScrollList.razor:49-52, a file only 43 lines long). Tracked as TD-16 in the backlog. One adversarially adjudicated non-move: §27 holds M4/I8 (a proposed impl 8→9 was re-proposed and re-rejected on the identical basis as the twenty-first cycle: PseudoLocalizationTests.cs:51 still covers exactly 3 public pages of 36 routable pages, unchanged since the prior rejection). This cycle also corrected drifted line anchors across the file (CI.slnf:56:58, deploy.yml:303-309/343/417:483-489/:783, e2e.yml:78:119, main.bicep:1113:1424 and :341:488). The twenty-first-cycle record follows for provenance: (twenty-first cycle, 2026-07-17, pin v1.117.0): two maturity lifts, both on gates shipped 2026-07-16 that close the exact blockers the twentieth cycle recorded: §13 Observability 3→4 (ObservabilityConventionTests, an ADC-local fitness function in the CI.slnf arch gate, machine-enforces the alert-to-runbook pairing between infra/main.bicep sloAlertSpecs and infra/OPERATIONS.md, converting the "review-enforced IaC, not CI-gated" maturity cap) and §22 Responsive & Cross-Browser 3→4 (the deploy-gating e2e-gate passes the full chromium+firefox+webkit matrix, deploy.yml:309, and e2e.yml:78 scopes continue-on-error to scheduled nightly non-chromium legs only, so every engine the gate invokes can fail a deploy; promoted 2026-07-16 after 8 consecutive fully green nightly matrices). One adversarially adjudicated non-move: §27 holds M4/I8 (the wave-6 PseudoLocalizationTests tier covers 3 public pages, a partial extension of the text-expansion evidence, so the recorded impl 8→9 candidacy was rejected). This cycle also corrects stale prose introduced 2026-07-17 by PR #15, which accidentally bundled a superseded nineteenth-cycle draft (a "§12 mat 4" strength claim, a risk-1 rewrite asserting the firefox/webkit legs cannot fail the gate, and a mislabeled backlog update paragraph); §12 stays M3/I8 per the twentieth-cycle adjudication, re-confirmed this run (its k6 tier is freshness-gated via load-freshness, deploy.yml:348,417, but the tier itself runs monthly/dispatch and the Notification app stays pinned maxReplicas: 1). The arch-test suite re-ran green this cycle (82/82 methods across 26 test classes; the three new methods are the ADC-local §13 gate). Indices: Maturity 96.6%→97.8% (313/320) / Implementation 86.3% (690/800, unchanged). The twentieth-cycle record follows for provenance: (twentieth cycle, 2026-07-15, pin v1.116.0) five scores moved, all up, each on evidence that postdates the nineteenth cycle. Three maturity lifts close stale "no gate exists" rationales: §18 UI Architecture 3→4 (UIArchitectureConventionTests now machine-enforces the code-behind/container split in the CI.slnf gate), §19 State Management 3→4 (StateManagementConventionTests enforces no-mutable-static-UI-state + scoped stateful services in the same gate), and §23 Front-End Performance 3→4 (the Core Web Vitals budgets were recalibrated 2026-07-11 into real failing assertions riding the deploy-gating chromium e2e-gate, superseding the advisory-by-design budgets the nineteenth cycle correctly rejected). Two implementation lifts: §13 Observability 8→9 (the two named gaps closed: the Azure Monitor SLO workbook dashboard and the per-alert infra/OPERATIONS.md runbook) and §24 Forms 7→8 (TD-14 shipped: per-form MudAlert error summaries on all six create forms, machine-enforced markers, and a dedicated Profile-form fitness test). Adversarially adjudicated non-moves: §28 holds M4/I8 (its row's false "E2E #5 un-skipped" claim and three drifted line anchors are corrected below; the test is re-quarantined at SpeakerSelfServiceTests.cs:57), §33 holds M3/I8 (a proposed impl 9 was rejected: broker parity local-RabbitMQ vs prod-Service-Bus is mitigated, not closed, per README.md:74), and §34 holds M4/I9 (the same 9→8 downgrade the nineteenth cycle rejected was re-proposed and re-rejected). The arch-test suite re-ran green this cycle (79/79 inherited methods across 25 thin-subclass classes, up from 74/23 on the two new §18/§19 gates). Indices: Maturity 94.1%→96.6% (309/320) / Implementation 85.8%→86.3% (690/800).

Update 2026-06-30 (under-8 Implementation lift — §12). One Implementation score moves up; Maturity holds: Maturity 94.1% (301/320) unchanged, Implementation 85.9% → 86.1% (689/800). §12 Performance & Scalability Implementation 7→8: a WebVitalsTests Playwright tier now measures client-side Core Web Vitals (LCP/CLS/FCP/TTFB + a single-interaction INP sample) on the home, public-events, and login pages and emits a dated web-vitals-*.json artifact, closing the "no measured client-side CWV/INP" gap so both the backend k6 and client halves are measured. This is a test/CI-only change (no MMCA.Common release needed); it builds clean against the framework source. §21 Accessibility holds at Implementation 7 this cycle: its named 7→8 lever (promoting axe to a merge gate) is scoped as a follow-up — a backend-less in-process axe host mirroring MMCA.Common's proven gallery-host pattern, plus a recorded manual screen-reader pass — rather than rushed here (see RemediationBacklog.md #21). The shared /login//register a11y surface is already chromium-merge-gated upstream in MMCA.Common's CI.

Executive summary

MMCA.ADC is a mature, evidence-driven .NET 10.0 (LangVersion preview) DDD/Clean Architecture system for the Atlanta Developers Conference, deliberately extracted from a modular monolith into four independently-hosted microservices (Identity, Conference, Engagement, Notification) behind a YARP gateway. Services collaborate synchronously via Result-over-the-wire gRPC contracts and asynchronously via MassTransit integration events on the outbox pattern, run database-per-service, and consume shared framework primitives as versioned MMCA.Common.* NuGet packages pinned uniformly at v1.123.0 (all 15). The standout characteristic is that architectural intent is not just documented but executable: 26 architecture-test classes (28 .cs files; 82 executed methods) enforce layer dependencies, domain purity, module isolation, transport-at-edge, concurrency, PII erasure, data residency, cross-source specification safety, UI architecture and state-management conventions, observability alert-runbook pairing, and resilience as CI gates; as of the framework's v1.73.0 these are thin subclasses of the shared MMCA.Common.Testing.Architecture rule library (ADR-015; 79 of the 82 methods inherited, the §13 alert-runbook pairing gate is ADC-local by design), so ADC, Store, and Common run the same rules rather than parallel copies. Capacity/cost decisions are sized to measured conference load (~67 peak) rather than guesswork.

This re-verification (2026-06-20) found the prior report materially stale. A remediation wave landed on 2026-06-19/06-20 that closes several of the previously-flagged top risks, each verified against source: (1) the disaster-recovery restore has now been drilledDISASTER-RECOVERY.md:140-144 records a 2026-06-20 PITR restore of ADC_Conference in 2.6 min (well within the 2 h RTO), automated via dr-drill.yml/scripts/dr-restore-drill.ps1, and TD-10 is closed — eliminating the rubric's #1 §29 red flag (untested restore); (2) the SessionSpeakers GetAll gap is closedSpeakerSelfServiceTests.cs:48-98 is now an active [Fact] (the Assert.Skip is gone) backed by a dedicated SessionIncludeChildrenRegressionTests; (3) a v2 API endpoint now existsServiceInfoController carries [ApiVersion("1.0", Deprecated)] + [ApiVersion("2.0")], demonstrating the versioning machinery beyond a single version; (4) EnableInbox=true on both consumers (Conference + Identity), activating Common's idempotent at-least-once delivery; (5) the PRIVACY.md residency contradiction is resolved — it now reads East US 2 apps / West US 2 SQL, matching the deployment, and is guarded by a new DataResidencyTests fitness function.

The headline finding remains a system whose backend and governance are near-exemplary, pulled down by a smaller-than-before cluster of unverified or unenforced operational claims. The weaker axis is still Implementation — an unusual inversion (process more complete than the proof those processes hold); through the v1.80.0 cycle the figures were Implementation 84.1% / Maturity 89.9%, the §32 lift took them to Implementation 84.3% / Maturity 90.5%, and this cycle's §29 lift to Implementation 84.7% / Maturity 90.5% — see Indices. The remaining points are won not by re-architecting anything but by promoting the high-quality but dispatch-only E2E/axe/a11y suites to merge gates (§21, §28, §22 — TD-06/07), closing structural supply-chain gaps like the missing lock files (§32 — TD-01), and hardening the SQL data-plane (§11/§17: public network access + runtime SQL admin login).

This re-verification (2026-06-25) found no score-moving change since 06-22. ADC was swept with the framework from v1.76.0 to v1.79.0 in three lockstep bumps (1.77/1.78/1.79), so it inherits Common's third-wave additions (polyglot-persistence plumbing, ADR-017 request-idempotency documentation, the Dependabot held-package guards). The one ADC-specific event worth recording is the polyglot trial-and-revert: ADC built and locally tested Conference Session→Cosmos DB and Room→SQLite (ADR-018's first-use trial), then deliberately reverted to all-SQL-Server while keeping every framework seam — the AppHost wires all four modules via WithSQLServerDataSource, every appsettings Cosmos/SQLite connection string is empty, and no entity uses a non-SQL engine shim. ADC did adopt the new cross-source SpecificationConventionTests opt-in, though its doc-comment still narrates the reverted Cosmos/SQLite layout — a cosmetic doc-drift, not a behavioral one. (Correction 2026-06-26: the prior cycle's claim that DataResidencyTests also narrates the reverted layout was wrong — that file's comment is about deployment-region residency only; SpecificationConventionTests.cs:3 is the sole arch-test still mentioning Cosmos/SQLite.) Net: the scorecard below is unchanged from 06-22 except for refreshed counts and evidence; the open items (E2E gate, lock files, SQL data-plane) are all re-confirmed open.

This re-verification (2026-06-26) found one substantive ADC-side change and no score move. ADC was swept from v1.79.0 to v1.80.0 (commit ac5b175, all 13 packages lockstep) and — unlike most framework capabilities, which it merely inherits — actively adopted the new permission-based authorization layer: [HasPermission("…")] now decorates ~20 Conference/Identity controller endpoints, services.AddPermissions(...) in both Conference.API/Identity.API DependencyInjection.cs:41 wires per-module capability sets (ConferencePermissions/IdentityPermissions in each module's Shared/Authorization/), and the new framework RoleNames.ContentEditor is granted a content-management permission subset (Conference.API/DependencyInjection.cs:50) — a genuine authorization-granularity gain (a content-editor role that could not exist under the prior four named role policies). ADC also took up the framework's TimeProvider adoption in three paths (Conference.Application/.../RefreshFromSessionizeHandler.cs, Conference.Domain/Events/Event.cs, Identity.Application/Users/AuthenticationService.cs). This enriches the §11/§1 evidence but moves no score: §11 remains held at impl 9 by the unchanged SQL data-plane red flags (below), which are orthogonal to the authz-richness gain. The BlazorCspPolicyProvider.cs Development-only CSP relaxation (localhost/ws://localhost connect-src + 'unsafe-inline' script-src for Hot Reload) noted as uncommitted last cycle is now committed (9c03871); the hardened production CSP is unchanged.

A subsequent wave (v1.81.0→v1.82.0, swept in commits b098f39c6bf4fa) is the basis for this re-verification, and it closed two long-standing structural gaps. (1) Lock files committed — TD-01 CLOSED (R7). ADC now has 58 packages.lock.json (commit 6248273) with RestorePackagesWithLockFile=true (Directory.Build.props:27); the two WASM host projects opt out by design. (2) Coverage floor enforced — TD-05 CLOSED. A 38% unit-tier line-coverage floor (measuring ADC's own +MMCA.ADC.*;-*.Tests code, ratcheted 24→38) now hard-fails the deploy-gating PR job (deploy.yml:67-84). (3) Supply-chain audit + SBOM are now blocking PR gates — the supply-chain job (deploy.yml:108-169, in deploy's needs and run on pull_request) fails the build on any non-suppressed vulnerability (:146 exit 1) or an empty SBOM (:155 exit 1). (4) Staged managed-identity SQL auth (Wave 3, default-OFF). main.bicep adds an additive Entra/AAD SQL admin (only when an object id is supplied, :360-369) and an opt-in useManagedIdentitySql connection-string mode (:35-36,131-133), with a SQL-MANAGED-IDENTITY.md runbook — but it is default-off, dual-auth (the SQL admin login is still provisioned, :337-338), azureADOnlyAuthentication is not set, and public network access (:341) + the AllowAzure 0.0.0.0 firewall rule (:345-352) are unchanged and documented as accepted, so the §11/§17 data-plane red flags persist in the deployed default state. (5) Notification pinned to one replica (Wave 3). notificationApp.scale = { minReplicas: 1, maxReplicas: 1 } (main.bicep:1012) with a documented single-replica acceptance (:1007-1011) — converting the prior "present-but-dormant SignalR backplane" latent risk into an explicit, documented constraint (the Redis backplane is still not wired). (6) The arch-test doc-drift is fixedSpecificationConventionTests.cs:3-7 now frames the Cosmos/SQLite trial as "was reverted... forward safeguard" rather than the current layout.

Open items re-confirmed in source (this cycle): the E2E/axe suite is still workflow_dispatch + nightly-only, NOT a PR/merge gate (e2e.yml:8-11,26-30 self-documents this; Common's v1.82.0 register/login de-flake is progress toward it but the gate is not flipped); the SQL data-plane hardening is staged-but-default-off (above); the SignalR Redis backplane is still not wired into the prod Notification app (single-replica acceptance documented); EnableInbox is true on Conference + Identity only (Engagement/Notification have no MessageBus block). Ledger refreshed (2026-06-26): the prior cycle's ledger-lag nit is resolved — RemediationBacklog.md marks #32/#14 (TD-01/TD-05) resolved, matching the code; and this ArchitectureScorecard.md is now the canonical two-axis evaluation in-repo (it replaced the former single-axis snapshot, which lives in git history). The backlog is now the single remediation + tech-debt ledger — the former TECHDEBT.md TD-NN register was folded into it 2026-06-26 (matching Common/Store). Effective TD status: done TD-01/TD-04/TD-05/TD-09/TD-10; open TD-02/03/06/07/08.

The v1.84.0 sweep (2026-06-27, HEAD e8510df) moved one score. ADC was swept from v1.82.0 to v1.84.0 (commits c6bf4fa17dce5ae8510df, all 13 packages lockstep). Two ADC-side additions landed in e8510df: a new MMCA.ADC.Gateway.Tests project (in CI.slnf) whose GracefulShutdownTests boots the real Gateway via WebApplicationFactory and asserts a bounded-time drain, and SecurityHeadersTests asserting the hardened Gateway headers + CSP/HSTS on every response; plus Engagement.UI.Tests/SessionFeedbackTests broadening feedback-form bUnit coverage. The graceful-shutdown test closes the last in-app failure-testing gap, lifting §29 Implementation 8→9 (maturity held at 3 — the DR restore drill is still workflow_dispatch-only, not a scheduled CI gate). No other score moved. Indices: Maturity 90.5% (unchanged) / Implementation 84.7% (up from 84.3%). This cycle also refreshed drifted evidence citations (the observability/SLO/budget/LTR bicep line numbers, the MMCA.Common.Aspire resilience source label, arch-test files now thin subclasses of the shared rule library) and the ADR-range reference (canonical 001-031 in MMCA.Common). The §26 header-regression gate (SecurityHeadersTests, CI.slnf) and the broadened §28 bUnit coverage reinforce existing scores but do not move them — the §26 deduction (two permissive CSP directives) and the §28/§21 maturity cap (E2E + axe still workflow_dispatch+nightly, not a merge gate) are both unchanged.

A subsequent wave (framework under-8 Implementation remediation) moves one ADC score. MMCA.Common shipped an eighth remediation wave lifting every framework category scored Implementation < 8; ADC adopts the shared, reusable piece — the new slice-cohesion fitness function (SliceCohesionTestsBase) — as a thin subclass that passes across all three modules (verified via local-source build), lifting §5 Implementation 7→8 (Implementation index 84.7%→84.9%). ADC's other two under-8 Implementation categories, §12 (no measured client-side Core Web Vitals; k6 is backend-only) and §21 (axe breadth + a documented screen-reader pass; the suite's promotion to a merge gate is the larger §21 maturity lever), are scoped but not yet moved — both need an interactive E2E/Aspire run, so they remain at 7 pending that work. (Shipped via the lockstep sweep to MMCA.Common. v1.85.0* — the release carrying the new slice-cohesion base.)

The v1.86.0 sweep (2026-06-27, HEAD d86cdf8) shipped the i18n + dark-mode wave and moved four scores. ADC was swept to MMCA.Common. v1.86.0* (all 13 packages, lockstep) and adopted the framework's en-US + es internationalization (ADR-027, which supersedes the single-locale ADR-011) and Day/Dark theming (ADR-028). The i18n adoption flips §27 Internationalization from N/A to scored (Maturity 3 / Implementation 8): 36 base .resx + 35 .es.resx across the three module UIs and three API error-resource sets, IStringLocalizer<T> in ~33 pages, culture-aware aligned SSR/Server/WASM, locale selection with cross-device persistence (User.PreferredCulture), and backend error localization keyed on Error.Code (SupportedCultures = [en-US, es]). The same wave wires dark mode functionally (bound IsDarkMode + ThemeToggle, PaletteDark) and makes the ADC landing page brand-token-clean (guarded by BrandColorTokenTests), lifting §20 Design System Implementation 8→9. §29 Resilience was recorded as moving Maturity 3→4 on the scheduled DR-drill cron (this v1.86.0-cycle claim was reversed back to Maturity 3 by the 2026-06-29 re-score below: a scheduled-but-non-gating dr-drill.yml cron is Consistent/M3, not an automatic CI gate). §24 Forms moves Implementation 8→9: the Profile change-password form gained Required + client-side match validation and a per-form error summary. The brute-force lockout (ADR-029), sole-migrator (ADR-030), and feature-flag (ADR-031) records enrich the §11/§8/§10 evidence respectively but move no score. A follow-on operational change on 2026-06-28 then activated managed-identity SQL auth in production (useManagedIdentitySql=true; all four services healthy on passwordless Active Directory Managed Identity connection strings, with the apps mapped db_owner in every per-service database), removing the shared SQL password and lifting §17 DevOps Implementation 8→9 (§11 Security holds at 9, still capped by the deferred-by-design public network access). Indices: Maturity 90.5→91.3% / Implementation 84.9→85.6% (see Indices).

The 2026-06-29 re-score (pin v1.92.0, HEAD 6b1239b, clean tree) corrected one prior overstatement and confirmed one code-caught-up lift. Re-scored strictly against the rubric from current source: §29 Resilience Maturity 4→3 (Implementation held at 9). The prior maturity-4 rested solely on calling dr-drill.yml a "scheduled CI-enforced gate," but the cron gates nothing. deploy.yml:284's deploy job needs only [build-and-test, integration-tests, supply-chain], and ADC's own CLAUDE.md:251 buckets dr-drill.yml among the "read-only and never deploy" operational workflows alongside e2e.yml. The scorecard already holds §28 at Maturity 3 for the identical "dispatch+nightly, not a merge gate" shape (e2e.yml:8-11), so crediting §29 Maturity 4 was internally inconsistent; the rubric's M4 ("enforced automatically by a CI gate") is not met by a scheduled-but-non-gating cron (ArchitectureEvaluationCriteria.md:28). The drill itself is genuine (a real Azure PITR restore, scripts/dr-restore-drill.ps1:63) and graceful shutdown is CI-gated (GracefulShutdownTests, in CI.slnf), so Implementation holds at 9; the open maturity-4 lever is to make the recovery proof actually gating. The one lift: §32 Supply-Chain Implementation 8→9. The scorecard's own stated 8→9 lever (adding --locked-mode) is already satisfied: both gating restores run it (deploy.yml:40, :119), so lock-file drift is tamper-enforced at restore; it is held below 10 only by MassTransit v8 being pinned transitively (inherited from Common, not in ADC's own props). Four categories whose first-pass scores drifted were adversarially re-checked and confirmed unchanged: §5 VSA (M3/I8, the deliberate layered-by-project red flag caps it), §7 Microservices (M4/I8, the bidirectional Conference↔Engagement gRPC pair), §13 Observability (M3/I8, the SLO workbook is pre-existing, not new this cycle), and §25 Navigation (M4/I8, the route-auth fitness tests are CI-gated, which is M4). Two stale evidence notes were refreshed without moving a score: §13's Aspire source/health anchors (Extensions.cs is now at :147/:264) and §25's "no dedicated 403 page" justification (a dedicated <Forbidden/> 403 page now exists, Routes.razor:26). Indices: Maturity 91.3→90.3% / Implementation 85.6→85.9% (see Indices).

The v1.93.0 sweep (2026-06-30, HEAD 89d8439, clean tree) moved one score. ADC was swept from v1.92.0 to v1.93.0 (the Common tenth-wave, all 13 packages lockstep). Re-scored strictly against the rubric from current source, the sweep moves §5 Vertical Slice Architecture Maturity 3→4: the slice-cohesion fitness function ADC subclasses (Tests/Architecture/MMCA.ADC.Architecture.Tests/SliceCohesionTests.cs:8, a sealed subclass of the shared SliceCohesionTestsBase) runs in the MMCA.ADC.CI.slnf arch gate (MMCA.ADC.CI.slnf:54) and fails the build when a handler/validator is stranded from its same-assembly contract (ArchitectureRules.Slices.cs:31), so slice cohesion is machine-enforced, meeting the rubric's M4 ("enforced automatically by tests/CI"). This matches the prior cycle's impl 7→8 lift with Optimized process maturity; Implementation holds at 8, capped by the deliberate layered-by-project Clean-Architecture spread (a feature still crosses Domain/Application/Infrastructure/API assemblies, a conscious hybrid). §7 Microservices Readiness was adversarially FLAG-re-checked: a first-pass proposed an impl 8→9 lift, which the verifier rejected, since the bidirectional Conference↔Engagement gRPC pair (real in AppHost/Program.cs:102) is a documented instance of the §7 "distributed monolith / chatty synchronous call graph" red flag, keeping it in the Strong 7-8 band, so §7 is confirmed unchanged at M4/I8. No other score moved on the sweep; the ADR set advanced to 001-034 (033-034 landed with the tenth wave) and the framework fitness-method count to the current FACTS figure, neither moving an ADC score. Indices: Maturity 93.4→94.1% / Implementation 85.9% (unchanged) (see Indices).

The 2026-07-02 re-score (pin v1.99.0, HEAD 3940656, clean tree) is a fourteenth-cycle honest recalibration: three scores move down, none up, on no framework sweep. Re-scored strictly against the rubric from current source: §18 UI Architecture & Components Maturity 4→3 (Implementation held at 9). The prior maturity-4 credited automatic enforcement, but no analyzer/test/CI gate asserts the §18 criteria: the ADC arch-test suite's only UI fitness functions cover §20 brand tokens (BrandColorTokenTests) and §24 forms (FormsConventionTests), and nothing enforces the container/presentational split, the code-behind convention, or component contracts, so it is a review-enforced convention (Consistent/M3), not Optimized/M4 (../MMCA.Common/ArchitectureEvaluationCriteria.md:29). The implementation is genuinely disciplined (32 of the 33 routable @page components have a code-behind; the row's prior '32 of 34 / Error.razor' text was inaccurate, since no Error.razor exists and only UserClaims.razor lacks a code-behind), so Implementation holds at 9. §6 CQRS & Event-Driven Design Implementation 10→9 (Maturity held at 4). The rubric reserves 10 for 'perfectly implemented, nothing left to improve' (../MMCA.Common/ArchitectureEvaluationCriteria.md:50); real levers remain, since EnableInbox=true is on only 2 of the 4 services (Conference + Identity; Engagement and Notification have no MessageBus block) and the SchemaVersion versioning path is present-but-unexercised (virtual … => 1, no event bumped to a v2), so the exemplary-band 9 is the accurate score and the prior 10 was slightly overstated. §27 Internationalization Implementation 8→7 (Maturity held at 4). The committed row justified I8 by calling the residual hard-coding 'exception-path English only', but current source shows it is broader: a success-path snackbar (Profile.razor.cs:139), a hard-coded page title + breadcrumbs (EventCreate.razor.cs:21), and a genuine §27 concatenation red flag (ErrorMessages.Success("Event", "created") composes "{0} {1} successfully." from literal English entity/action words), plus no text-expansion/layout evidence, so I7 is better calibrated. The CI-gated translation-completeness fitness function keeps Maturity at 4. No score moved up. Indices: Maturity 94.1→93.1% / Implementation 85.9→85.8% (see Indices).

The same-day fifteenth-cycle pass (2026-07-02 drift-plan docs sweep, D12/D15) reflected the e2e-gate promotion (validation run 28604877733, the first fully green three-browser matrix; e2e.yml gained workflow_call and deploy.yml an e2e-gate job in deploy.needs): §28 Maturity 3→4 (the chromium E2E/axe suite now gates every deploy), §21 Maturity 2→3 + Implementation 7→8 (the axe layer is enforced through the same gate and the broadened 17-page scans have their green run; Maturity stops at 3 pending the recorded manual screen-reader pass the rubric pairs with automated CI checks), and §19 State Management Maturity 4→3 (honest recalibration mirroring §18: reference-quality substance, but no §19 fitness gate exists, so the conventions are review-enforced, Consistent not Optimized). Indices moved to Maturity 94.1% (301/320) / Implementation 86.1% (689/800).

The 2026-07-03 re-score (pin v1.101.0, HEAD ac43c8d8, clean tree) is a sixteenth-cycle full 34-category re-verification; one score moves, down. ADC was swept from v1.99.0 through v1.100.0 to v1.101.0 (all 14 packages lockstep). Every category was re-scored strictly against the rubric from evidence read this run; two first-pass moves were adversarially re-checked, and §20 was corrected back to unchanged at M4/I9. The one confirmed move: §24 Forms, Validation & UX Safety Implementation 9→7 (Maturity holds 4). The prior exemplary-9 overstated error presentation: the per-form error summary the row credited exists only in the Identity Profile form (Profile.razor:41-52), while all six Conference admin create forms surface only a generic validation snackbar (EventCreate.razor.cs:54 routes handler failures to ErrorMessages.ValidationError), and the Profile handlers interpolate raw exception text into user-facing snackbars (Profile.razor.cs:67,114,148 embed {ex.Message}). A systematic gap across the primary authoring surface puts the substance at the bottom of the Strong (7-8) band, not reference quality. Maturity stays 4: FormsConventionTests (CI.slnf arch gate) still machine-enforces the guard/dirty/validated-form conventions, but it deliberately excludes the Profile form (FormsConventionTests.cs:7-8) and does not assert error-summary presence; extending it plus sanitizing the exception snackbars is the new TD-14 in RemediationBacklog.md. The remaining 33 categories were confirmed at their prior scores, including the open-maturity set (§12/§13/§18/§19/§21/§22/§23/§33 all confirmed M3). Indices: Maturity 94.1% (301/320, unchanged) / Implementation 86.1%→85.6% (685/800) (see Indices).

The same-day seventeenth-cycle i18n completion sweep (2026-07-03, ADR-027 Decision 9) lifts §27 Implementation 7→8. Riding the framework's i18n completion train, ADC externalized every remaining user-visible literal: all ~24 hard-coded page titles adopt Store's Title => L["Title"].Value pattern, all 56 hard-coded snackbars and every ErrorMessages.Success fragment-composition call (28 sites, now [Obsolete] upstream) become whole-sentence page resource keys with raw {ex.Message} never surfacing (the TD-14 sanitization half), all literal breadcrumbs are built in OnInitialized from Breadcrumb.* keys, both ADCHome hosts and the module nav items localize (NavItem.TitleResource + new module resx pairs), and MudBlazor built-in chrome localizes via the inherited ResxMudLocalizer. Two CI gates hold the state: the completeness gate's floor rises 30→40 and the new LocalizedTextConventionTests (a LocalizedTextConventionTestsBase subclass) fails the build on any new hard-coded snackbar/title/<PageTitle>/breadcrumb/NavItem literal. Verified: full CI suite 1279/1279 green post-sweep. §24's Implementation stays 7 (TD-14 narrows to the fitness-gate Profile exclusion + per-form error summaries; the raw-exception half is done). Indices: Maturity 94.1% (unchanged) / Implementation 85.6%→85.8% (686/800) (see Indices).

The 2026-07-06 re-score (eighteenth-cycle full 34-category re-verification, pin v1.106.0, HEAD 8fc9e0d2) moved no score. ADC was swept from v1.101.0 through v1.106.0 (all 14 packages lockstep) and absorbed two test-coverage programs: a ~900-case unit/UI wave with a cross-repo defect-fix pass (A-1..A-7, recorded in RemediationBacklog.md), and a ~174-test integration-coverage expansion adding the MMCA.ADC.CrossService.IntegrationTests broker round-trip tier and the MMCA.ADC.Notification.IntegrationTests service tier. Every category was re-scored strictly against the rubric from evidence read this run (a three-part rubric-scorer fan-out, indices re-summed by hand, plus an adversarial evidence-verifier pass on the one state-changing claim), and all 34 confirmed at their prior Maturity/Implementation. The one closed backlog item: §8's TD-03 optimistic-concurrency API round-trip is now implemented and deploy-gated (Conference EventDTO carries the RowVersion token via IConcurrencyAware, UpdateEventHandler.cs:34 stamps it with SetOriginalRowVersion, and OrganizerConcurrencyTests.cs:26 asserts a stale token returns 409 inside the deploy-gating MMCA.ADC.Integration.slnf), which updates §8's Implementation-9 cap reason (the round-trip is adversarially confirmed Conference-only, so 9 not 10 still holds) without moving its score. The rest of the cycle is an evidence refresh: the arch-test suite is now 23 thin-subclass test classes across 25 .cs files executing 74 inherited methods (0 ADC-local), up from the 19-class / 71-method snapshot; the §14 tiers grew to 1507 unit/UI [Fact]/[Theory] across 223 files and 303 gating integration methods across four per-service tiers (Identity 62 / Conference 210 / Engagement 17 / Notification 14) plus 9 non-gating CrossService tests, and the unit-tier coverage floor rose 38→55.5% (actual ~57%); the ADR set advanced to 001-038 (037 field-level encryption at rest, 038 supply-chain provenance); the §27 resx pairs are 40 base + 40 es; and several stale evidence citations were corrected in place (§24's now-localized snackbars, §26's CSP/token services having moved into MMCA.Common). Indices: Maturity 94.1% (301/320) / Implementation 85.8% (686/800), both unchanged (see Indices).

The 2026-07-10 re-score (nineteenth-cycle full 34-category re-verification, pin v1.110.0, HEAD 246a24dc, clean tree) moved no score. ADC was swept from v1.106.0 to v1.110.0 (all 14 packages lockstep, Directory.Packages.props:112-128), absorbing the live-layer waves (ADR-039 hub channels) and the runtime-performance wave. Every category was re-scored strictly against the rubric from evidence read this run (a one-scorer-per-category fan-out plus an adversarial evidence-verifier pass on every proposed move), and all 34 confirmed at their prior Maturity/Implementation. Three first-pass proposals were adversarially rejected, each a verified non-move: §12 Implementation 8→9 rejected. The wave added genuine scale substance: Azure Managed Redis (Balanced B0) as the shared distributed cache (infra/main.bicep:511,516), the three REST services at maxReplicas: 2 (:781,:881,:981) with cross-replica idempotency replay and cache invalidation (:500-508), ADR-040 authenticated output caching, and ReadyToRun publishes (Source/Services/*/Dockerfile:31); but the Notification app stays pinned maxReplicas: 1 (:1113), a live scale-out constraint on the real-time hub that keeps §12 in the Strong band, and the k6/vitals tiers stay nightly/dispatch (Maturity 3). §23 Maturity 3→4 rejected. The WebVitals budgets are deliberately lenient/advisory (WebVitalsTests.cs:14-24), no §23-specific fitness gate exists, and its presence inside the chromium e2e-gate is incidental to §28's promotion, so the conventions remain review-enforced (Consistent, not Optimized); a §23-only lift while §12 holds M3 on identical evidence would be internally inconsistent. §34 Implementation 9→8 rejected. No governance regression exists: the ADR set is current through 041, the 23 arch-test classes gate CI (MMCA.ADC.CI.slnf:55), the scorecard/backlog are versioned in-repo, and the sole residual (ArchitecturalAnalysis.md at the untracked workspace root) is the already-weighed 9-not-10 lever. The cycle also fixed a genuine infra doc contradiction found this run: the Notification scale-pin comment claimed the hub has no Redis backplane while ConnectionStrings__redis is injected at :1056 and the Managed Redis block records that the backplane auto-wires on that key (:507-508); the comment now documents the pin as a deliberate right-sizing at the measured ~67 peak. Evidence refresh: ADR range 001-041 (039 live hub channels, 040 authenticated output caching, 041 observability and telemetry), pin v1.110.0, §12's dead anchors corrected, and the arch-test suite re-run green this cycle (74/74). Indices: Maturity 94.1% (301/320) / Implementation 85.8% (686/800), both unchanged (see Indices).

The 2026-07-15 re-score (twentieth-cycle full 34-category re-verification, pin v1.116.0, HEAD 913d088a, clean tree) moved five scores, all up. ADC was swept from v1.110.0 to v1.116.0 (all 14 packages lockstep), absorbing the framework release carrying ADRs 046-048 and the new shared §18/§19 architecture-test bases. Every category was re-scored strictly against the rubric from evidence read this run (one rubric-scorer per category plus an adversarial evidence-verifier pass), and the work landed between the cycles converted three honest-recalibration maturity holds back to Optimized by shipping the exact gates whose absence had justified them: §18 UI Architecture Maturity 3→4 (UIArchitectureConventionTests.cs:12, a sealed subclass of the shared UIArchitectureConventionTestsBase, machine-enforces the code-behind/container split; the arch-test project gates every PR via MMCA.ADC.CI.slnf:56 + deploy.yml:57), §19 State Management Maturity 3→4 (StateManagementConventionTests.cs:11 enforces zero mutable static UI state and scoped stateful services via the shared base's two rules, same gate; Implementation holds at 9 with no exemptions declared, and a first-pass proposal to drop it to 8 was adversarially rejected as an unsupported downgrade), and §23 Front-End Performance Maturity 3→4 (the Core Web Vitals budgets were recalibrated 2026-07-11 into real failing assertions, WebVitalsTests.cs:76, running unfiltered inside the deploy-gating chromium e2e-gate, deploy.yml:306,417: substantively different evidence from the advisory-by-design budgets the nineteenth cycle correctly rejected). Two implementation lifts: §13 Observability 8→9 (both named gaps closed: the Azure Monitor SLO workbook, infra/main.bicep:296, and the per-alert day-2 Operations Runbook, infra/OPERATIONS.md, added 2026-07-11 after the prior scorecard edit; Maturity holds 3, these remain review-enforced) and §24 Forms 7→8 (TD-14 shipped: all six Conference create forms carry the per-form MudAlert error summary, EventCreate.razor:108, the FormsConventionTests markers enforce it plus a new dedicated Profile-form test, FormsConventionTests.cs:18,32, and the raw-exception snackbars are localized, Profile.razor.cs:235). Non-moves worth recording: §28 holds M4/I8 but its row carried a false claim now corrected (E2E #5 is re-quarantined with an active [Fact(Skip)] at SpeakerSelfServiceTests.cs:57 over a stale SessionsCache read on the speaker dashboard, not "un-skipped"; the CI.slnf UI.Tests anchors are :43/:49/:55 and the deploy needs list is deploy.yml:417); §33 holds M3/I8 (the README-stub friction is genuinely resolved, but a proposed impl 9 was rejected because broker parity, RabbitMQ local vs Azure Service Bus prod, is mitigated-not-closed by the README's own admission, README.md:74, a live rubric red flag); §34 holds M4/I9 (the identical 9→8 downgrade the nineteenth cycle rejected was re-proposed and re-rejected). Evidence refresh: ADR range 001-048 (046-048 new), pin v1.116.0 (FrameworkVersionConsistencyTests confirms no divergence), §13's drifted main.bicep/Extensions.cs anchors corrected in place, and the arch-test suite re-run green this cycle (79/79 methods across 25 thin-subclass classes; the two new classes are exactly the §18/§19 gates). Indices: Maturity 94.1%→96.6% (309/320) / Implementation 85.8%→86.3% (690/800) (see Indices).

Scorecard

# Category Weight Maturity (0-4) Impl (0-10) Weighted (M·w / I·w) Evidence / Notes
1 SOLID Principles 3 4 9 12/27 SRP/OCP/DIP exemplary and machine-enforced: tiny cohesive handlers, Strategy for variation, abstraction-only injection (now incl. TimeProvider over ambient DateTime.UtcNow in RefreshFromSessionizeHandler/AuthenticationService, v1.80.0), NetArchTest layer rules gated in CI; narrow ISP interfaces. Minor LSP/ISP nuances (a few Delete() overrides) keep it off 10. — Conference.Application/Events/UseCases/Publish/PublishEventHandler.cs:14-15 (ctor = IUnitOfWork+ILogger only); Conference.API/Controllers/EventQuestionAnswersController.cs:56-63 (injects handlers/IEntityQueryService<>, never repos); .../RefreshFromSessionize/{ISessionizeSyncStrategy,SpeakerSyncStrategy}.cs (per-entity Strategy, no growing switch); now also a ctor-dependency-ceiling fitness function Tests/Architecture/MMCA.ADC.Architecture.Tests/ConstructorDependencyCountTests.cs (≤7, with AuthenticationService at the 7 high-water mark)
2 Design Patterns 2 4 9 8/18 Creational (entity factories/Options), structural (decorator pipeline, manual mapper adapter, gRPC adapters), behavioral (Strategy, dispatcher, Specification, domain events), and domain patterns (Result, Repository/UoW, Outbox) all present, idiomatic, ADR-justified, consistently named. — Identity.Domain/Users/User.cs:130 (Factory Create→Result<User>); Conference.Application/Events/Specifications/OwnEventQuestionAnswerSpecification.cs:15-16 (authz scoping); exactly 5× {Category,Room,Speaker,Session,Question}SyncStrategy.cs
3 Clean Architecture 3 4 9 12/27 Dependency rule inward-pointing, domain framework-pure, ports in Application/adapters in Infrastructure, rules enforced automatically by NetArchTest gated in CI — textbook; only the rich per-layer project sprawl keeps it shy of 10. — Tests/Architecture/MMCA.ADC.Architecture.Tests/{LayerDependencyTests,DomainPurityTests,MicroserviceExtractionTests}.cs (thin sealed subclasses of the shared MMCA.Common.Testing.Architecture rule library, ADR-015 — Domain↛App/Infra/API and App↛Infra/API per module, domain framework-purity, transport-at-edge; all in MMCA.ADC.CI.slnf)
4 Domain-Driven Design 3 4 9 12/27 Aggregates enforce invariants internally, reference each other by ID, use value objects (Email) + identifier aliases, raise domain events, expose rich behavior with Result-returning factories — no anemic model; ubiquitous language (BR-### business rules) pervasive. Money/Address VOs live in Common rather than ADC, a minor locality nit. — Identity.Domain/Users/User.cs:16,20,130 (rich aggregate, IAnonymizable, Email VO, Result factory, domain events :267/:305); Conference.Domain/Events/Event.cs:55-71,424,445 (encapsulated child collections, by-ID refs, EventSpeakerChanged events); Conference.Domain/Speakers/Speaker.cs:7,24 (Email VO from Common)
5 Vertical Slice Architecture 2 4 8 8/16 ↑ maturity 3→4 (this cycle): slice cohesion is now an enforced CI merge gate, so process maturity is Optimized (impl held at 8). Within Application, features are genuine cohesive slices (command+validator+handler+DTO together, low inter-slice coupling, pipeline-handled cross-cutting), and slice cohesion is machine-enforced: ADC subclasses the shared SliceCohesionTestsBase fitness function (MMCA.Common.Testing.Architecture), which passes across all three modules (verified), failing the build if a handler/validator is stranded from its same-assembly contract; the test runs in MMCA.ADC.CI.slnf so it gates every push/PR (the rubric's M4 "enforced automatically by tests/CI"). Held at impl 8 (not 9): the overall solution is deliberately layered-by-project Clean Architecture, so a feature still spreads Domain/Application/Infrastructure/API across assemblies (a conscious hybrid). Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/SliceCohesionTests.cs:8; MMCA.ADC.CI.slnf:54; ArchitectureRules.Slices.cs:31; Conference.Application/Events/UseCases/AddEventQuestionAnswer/; Application/{Aggregate}/UseCases/{Operation}/ uniform across modules
6 CQRS & Event-Driven 2 4 9 8/18 ↓ Implementation 10→9 (fourteenth-cycle recalibration): the rubric reserves 10 for 'nothing left to improve' (../MMCA.Common/ArchitectureEvaluationCriteria.md:50) and real levers remain, so exemplary-9 is the accurate score (Maturity holds at 4). Commands (Result, ITransactional) and queries cleanly separated via a documented, enforced decorator pipeline; outbox gives atomic persist-then-publish with smart-wait/retry/dead-letter; the integration-event wire contract is frozen by a CI fitness function; events carry a Common-supplied SchemaVersion. Consumer idempotency is infrastructure-backed (EfInboxStore dedup) via EnableInbox=true on all four services now (Conference/Identity/Engagement/Notification; Engagement + Notification joined on the wave-6 TD-02 close, appsettings.json), but the versioning path is present-but-unexercised (SchemaVersion is virtual … => 1, no event has bumped to a v2), and the genuine broker round-trip covered by MMCA.ADC.CrossService.IntegrationTests (TD-02) runs only in a non-gating nightly (cross-service-tests.yml) whose recency is enforced by a cross-service-freshness deploy check rather than the tier itself gating, so real improvement remains and it sits at exemplary 9 not perfect 10. Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/IntegrationEventContractTests.cs:9-14 (frozen contract, CI-gated via MMCA.ADC.CI.slnf:56); EnableInbox=true on all four service appsettings.json (Identity/Conference/Engagement/Notification); Common BaseIntegrationEvent.cs:22 (SchemaVersion virtual, default 1); Common OutboxProcessor.cs; ADR-003
7 Microservices Readiness 3 4 8 12/24 Service boundaries align with bounded contexts, each owns its own DB (no shared writable schema), integration async-via-outbox + sync-via-versioned-gRPC, resilience a CI-enforced invariant, transport-at-edge keeps modules extractable/reversible. Only smell: the bidirectional Conference↔Engagement gRPC pair (documented, self-healing via the resilience pipeline; AppHost deliberately omits the reciprocal WaitFor). — ADR-008/006/007; Conference.Contracts/Protos/session_bookmark_validation.proto + Engagement.Contracts/Protos/bookmark_count.proto (the pair) + Identity.Contracts/Protos/attendee_query.proto; Tests/Architecture/MMCA.ADC.Architecture.Tests/MicroserviceExtractionTests.cs (transport-at-edge guard, thin subclass of the shared rule library)
8 Data Architecture 3 4 9 12/27 ↑ from impl 8. Transaction boundaries match aggregates (UoW + per-source best-effort), soft-delete + audit + RowVersion concurrency applied centrally by convention and locked by a build-time fitness function, migrations versioned/idempotent with a model-drift CI gate, per-service isolation + navigation populators address cross-context joins and N+1. The long-standing SessionSpeakers GetAll gap is closed: SessionIncludeChildrenRegressionTests.cs:27 proves GET /sessions?includeChildren populates sessionSpeakers (the E2E-level SpeakerSelfServiceTests.cs second journey is re-quarantined at :57 over a stale SessionsCache read, a UI-caching matter tracked under §28; the data-layer fix itself stays guarded). ADC also opts into Common's new cross-source SpecificationConventionTests (the polyglot safety gate, ADR-018), though it routes all entities to SQL Server: the Cosmos/SQLite trial was reverted, with the framework extension points kept (every appsettings Cosmos/SQLite connection string is empty; AppHost uses WithSQLServerDataSource for all four modules). Held at 9 (not 10) because the optimistic-concurrency round-trip is now implemented and deploy-gated (TD-03 closed) but is exercised on Conference update endpoints only (Identity/Engagement expose none). Evidence: Conference.Shared/Events/EventDTO.cs:9,16 (RowVersion token via IConcurrencyAware), Conference.Application/Events/UseCases/Update/UpdateEventHandler.cs:34 (SetOriginalRowVersion), Tests/Integration/MMCA.ADC.Conference.IntegrationTests/Organizer/OrganizerConcurrencyTests.cs:26 (stale-token→409, in the deploy-gating Integration.slnf); Common ApplicationDbContext.cs:149-196; ConcurrencyConventionTests.cs; SpecificationConventionTests.cs (cross-source gate, active)
9 API & Contract Design 2 4 9 8/18 ↑ from impl 8. Consistent shapes, standardized RFC 9457 errors, edge validation, manual DTO decoupling (ADR), uniform pagination/sorting/filtering, OpenAPI served per service at /openapi/v1.json and snapshot-guarded in CI, async integration-event contract frozen. A v2 contract now existsServiceInfoController.cs:19-20 ([ApiVersion("1.0", Deprecated)] + [ApiVersion("2.0")], ReportApiVersions), demonstrating breaking-change evolution beyond the uniform v1.0. Held below 10 because v2 is demonstrated on one endpoint only (20 controllers total, the rest at v1.0). — EventsController.cs:35,80,87-88,111 (ApiVersion, OutputCache, [Range], X-Pagination); Tests/Integration/.../Contract/OpenApiContractTests.cs:44-56
10 Cross-Cutting Concerns 2 4 9 8/18 Validation/logging/transactions/caching/resilience/mapping/config are every one centralized via decorators, shared middleware, typed+startup-validated options, an ADR-decided mapping policy, and a CI-enforced resilience invariant — all §10 criteria, none of the duplication/magic-string/uninvalidated-cache red flags. — Common Application/DependencyInjection.cs:89-106 (pipeline); Common Infrastructure/DependencyInjection.cs:60-246 (typed options ValidateOnStart); EventsController.cs:179-268 (cache eviction guarded by IsFailure early-return); ADR-001
11 Security 3 4 9 12/27 Algorithm-pinned RS256/JWKS auth with no shared key, server-side policy+owner authz gated by ~55 authz integration tests, all prod secrets in Key Vault via managed identity (no admin creds for ACR), parameterized EF, audit-source-scoped NuGetAudit + SBOM in CI. Now finer-grained via the adopted v1.80.0 permission layer: [HasPermission] on ~20 Conference/Identity endpoints over per-module capability sets (AddPermissions + ConferencePermissions/IdentityPermissions), with a real RoleNames.ContentEditor role granted a content-management subset — capability-based authz layered on roles, server-enforced. It also inherits Common v1.82.0's RS256 pin on the JWKS-forwarded auth path (defense-in-depth against alg-confusion on cross-service tokens). Held at 9 (not lifted to 10): managed-identity SQL auth is now ACTIVE in production (2026-06-28, useManagedIdentitySql=true) — all four services run on Authentication=Active Directory Managed Identity with mapped as db_owner in every per-service database (verified Healthy on the new revisions), so the shared SQL password is gone from every connection string and the runtime SQL admin login is no longer used by the apps. That closes the most credential-significant data-plane flag. The cap remains because the network-exposure flags are unchanged and deferred by design: publicNetworkAccess: 'Enabled' (:341) + the AllowAzure 0.0.0.0 firewall rule (:345-352) (no VNet/private endpoint, documented-accepted in infra/SQL-MANAGED-IDENTITY.md), and azureADOnlyAuthentication is not set (the password path is retained as a dormant fallback). — Identity.Service/Program.cs:129 (AddCommonAuthentication RS256/JWKS); Conference.API/DependencyInjection.cs:41,50 (AddPermissions + ContentEditor grant); Conference.API/Controllers/EventsController.cs ([HasPermission], 16 endpoint usages); Engagement.API/.../BookmarksController.cs:31,66,88,114 (authz + owner-vs-Organizer); infra/main.bicep:532-586 (KV secrets via UAMI), :516-519 (ACR via UAMI, no admin creds); ✓ MI SQL auth ACTIVE (useManagedIdentitySql=true, password dropped, apps db_owner via); ⚠ :341 publicNetworkAccess Enabled + :345-352 AllowAzure 0.0.0.0 retained (accepted, SQL-MANAGED-IDENTITY.md)
12 Performance & Scalability 2 3 8 6/16 Uniform async, server-side paging + AsNoTracking + output caching on hot read paths, Polly resilience, and an actual k6 load test in CI sized to measured conference peak rather than guesswork. The v1.106.0→v1.110.0 runtime-performance wave added real scale substance (verified this cycle): Azure Managed Redis (Balanced B0) is the shared distributed cache (infra/main.bicep:511,516), the three REST services scale to maxReplicas: 2 (:781,:881,:981) with idempotency replay records and cache invalidation made cross-replica by the shared Redis (:500-508), Conference reads gained ADR-040 authenticated output caching, and every service publishes ReadyToRun (Source/Services/*/Dockerfile:31). The client-side half is measured: the WebVitalsTests Playwright tier captures LCP/CLS/FCP/TTFB (plus a single-interaction INP sample) on /, /conference/events, and /login, asserts them against deliberately lenient budgets, and emits a dated web-vitals-*.json artifact. Held at Implementation 8 (a proposed 8→9 adversarially rejected this cycle): the Notification app stays pinned maxReplicas: 1 (:1113), so the real-time hub cannot scale horizontally even though the backplane key is injected (:1056) and Common auto-wires the StackExchange.Redis backplane on it (:507-508); the pin is a deliberate right-sizing at the measured ~67 peak (its formerly contradictory comment corrected this cycle) but remains a live scale-out constraint until a two-replica fan-out is verified. Maturity held at 3: like the k6 test, the vitals run nightly/dispatch, not as a merge gate. Evidence: Tests/E2E/MMCA.ADC.E2E.Tests/Workflows/WebVitalsTests.cs:18 (the vitals tier; the formerly cited Infrastructure/WebVitalsCollector.cs does not exist); .github/workflows/e2e.yml:213 (WEB_VITALS_OUTPUT_DIR uploaded artifacts); MMCA.Common.Aspire/Extensions.cs:41-52 (Polly on all HttpClients); Tests/Load/k6/conference-read-load.js:35-36 (p95<800ms, <1% err); .github/workflows/load-test.yml:13 (PEAK_VUS 67 = observed 2026 peak); infra/main.bicep:511,781,881,981,1056,1113
13 Observability & Operability 2 4 9 8/18 ↑ Maturity 3→4 (twenty-first cycle): the alert-to-runbook governance surface is now a CI-gated fitness function, closing the exact "review-enforced, not CI-gated" cap the twentieth cycle recorded. Structured logging + OTel traces/metrics with correlation, liveness/readiness probes driving ACA, workspace App Insights, codified SLO alerts + action group + cost budget in IaC, deliberate telemetry noise/cost control, plus the twentieth-cycle impl lifts: an Azure Monitor SLO workbook (dashboard) in IaC (infra/main.bicep:296) and a dedicated per-alert Operations Runbook (infra/OPERATIONS.md:13 per-alert triage for failed-requests/response-time/dependency-failures, :55 recovery moves). Maturity is now 4: ObservabilityConventionTests (shipped 2026-07-16, ADC-local) embeds infra/main.bicep + infra/OPERATIONS.md as manifest resources (MMCA.ADC.Architecture.Tests.csproj:20) and fails the build when any sloAlertSpecs entry lacks a severity-correct runbook section or a runbook section is orphaned (ObservabilityConventionTests.cs:34), with a minimum-spec floor of 3 keeping the parse non-vacuous (:24); the test runs in the CI.slnf arch gate (MMCA.ADC.CI.slnf:56, executed by deploy.yml:57, gating deploy via deploy.yml:417), meeting the rubric's M4 automatically-enforced bar. Implementation holds 9 (runbook/workbook substance unchanged). Evidence: MMCA.Common.Aspire/Extensions.cs:153,158 (MMCA.Common.Outbox meter + telemetry source), :270,274,283 (/health + /alive + /health/ready split; all consumed via AddServiceDefaults()); infra/main.bicep:173 (workspace App Insights), :208 (action group), :253 (3 SLO metricAlerts, specs :226-251), :296 (SLO workbook), :317 (monthly cost budget); infra/OPERATIONS.md:1
14 Testability & Test Strategy 3 4 9 12/27 Healthy non-inverted pyramid (1507 unit/UI [Fact]/[Theory] across 223 files — incl. the new headless MMCA.ADC.Gateway.Tests tier (SecurityHeaders + GracefulShutdown) in CI.slnf; 82 architecture fitness-function methods across 26 test classes (28 .cs files); 79 inherited from the shared MMCA.Common.Testing.Architecture bases via thin sealed subclasses (the formerly-local DataResidencyTests/ConstructorDependencyCountTests/BrandColorTokenTests subclass shared bases too) plus the 3 deliberately ADC-local ObservabilityConventionTests methods (the §13 alert-runbook pairing gate), 303 gating integration methods across four per-service WAF tiers (Identity 62 / Conference 210 / Engagement 17 / Notification 14), plus 9 non-gating CrossService broker round-trip tests (Testcontainers RabbitMQ+SQL, nightly cross-service-tests.yml), shared E2E infra) with deterministic CI gates; the integration tier gates every deploy. (The ~495 integration count in the prior report double-counted the orphaned legacy combined Tests/Integration/MMCA.ADC.IntegrationTests dir, which has now been deleted (a minor §16 cleanup); the live per-service tiers now hold 303 gating methods across four projects.) TD-05 is now closed — a 55.5% unit-tier line-coverage floor (ADC's own code, ratcheted to 55.5, actual ~57%) hard-fails the deploy-gating PR job (deploy.yml:83), so coverage is no longer report-only. The former soft gap is closed: the chromium E2E suite gates deploy via the e2e-gate job (promoted 2026-07-02). Held at impl 9 (not 10) because the genuine broker round-trip is non-gating nightly and the SQL integration suite runs only in CI (not headless-verifiable). Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/; Tests/Integration/MMCA.ADC.{Identity,Conference,Engagement,Notification}.IntegrationTests + MMCA.ADC.CrossService.IntegrationTests; deploy.yml:344 (deploy needs [build-and-test, integration-tests, supply-chain, cost-guard, dr-freshness, e2e-gate]), :83 (coverage floor 55.5%, gating); e2e.yml (workflow_call + nightly matrix); cross-service-tests.yml (non-gating nightly)
15 Best Practices & Code Quality 2 4 8 8/16 Five analyzers + warnings-as-errors + AnalysisMode=All enforced in build/CI, central package management, consistent Result-pattern error handling, only justified/tracked suppressions. Minor deductions for the preview LangVersion surface and a small set of documented/dated NoWarn + NuGetAuditSuppress entries. — Directory.Build.props:16,18,20 (TWAE, AnalysisMode=All, EnforceCodeStyleInBuild), :37-56 (five analyzers at error), :8-12,32-34 (NuGetAuditSuppress targeted to specific MAUI/SQLite advisories, justified+dated 2026-06-19); Directory.Packages.props:4 (CPM)
16 Maintainability & Evolvability 2 4 8 8/16 ↑ maturity 3→4 (this wave): an automated evolvability gate now keeps the lockstep framework version from drifting. Boundaries auto-enforced, the shared framework consumed at a single coordinated version (no divergence), and the why/what documented via current ADRs plus a serviced tech-debt ledger. Drift is minor and cosmetic, and two items closed this cycle: the arch-test doc-comment (SpecificationConventionTests.cs:3-7) is now fixed (frames the reverted Cosmos/SQLite trial as a "forward safeguard," commit 9c03871), and the ledger lag is resolvedRemediationBacklog.md was refreshed 2026-06-26 to mark TD-01/TD-05 (#32/#14) done, matching the code. The backlog is now the single remediation + tech-debt ledger (the former TECHDEBT.md TD-NN register was folded into it 2026-06-26, matching Common/Store). Residual drift is now just the frozen combined Migrations archive csproj (the orphaned non-building combined Tests/Integration/MMCA.ADC.IntegrationTests dir was deleted this cycle, a minor §16 cleanup; this scorecard is the canonical in-repo two-axis evaluation, no longer a stale snapshot). Process maturity is now Optimized: FrameworkVersionConsistencyTests.cs fails the build (it runs in the CI.slnf arch gate) if any MMCA.Common.* package diverges from the single lockstep version, making ADR-016 executable rather than a convention. — Tests/Architecture/MMCA.ADC.Architecture.Tests/FrameworkVersionConsistencyTests.cs; Tests/Architecture/.../{LayerDependency,ModuleIsolation}Tests.cs; Directory.Packages.props:121-139 (all 15 MMCA.Common.* uniformly at 1.117.0, confirmed no divergence); RemediationBacklog.md (single ledger; ADRs live canonically in MMCA.Common)
17 DevOps & Deployment 2 4 9 8/18 ↑ impl 8→9. CI gates build/analyzers/unit+arch/integration/model-drift/coverage-floor on every PR, and the supply-chain vuln-audit + SBOM are now blocking PR gates (no longer report-only); CD fully automated via multi-phase Bicep IaC with OIDC + managed identity (no standing creds); post-deploy smoke gate with revision-copy auto-rollback plus a scheduled cost-drift guard. App→DB auth is now passwordless managed identity in production (useManagedIdentitySql=true, 2026-06-28), so the CD path no longer ships a runtime SQL admin login/password to the apps. Held below 10 only by the deferred-by-design SQL public network access (:341, accepted, no VNet). — deploy.yml:3-8 (push+PR+dispatch), :89-102 (model-drift gate), :67-84 (coverage floor), :108-169 (blocking supply-chain audit+SBOM), :344 (deploy needs build-and-test+integration+supply-chain+cost-guard+dr-freshness+e2e-gate), :509-571 (Phase-5 smoke + auto-rollback), OIDC no standing creds; ✓ passwordless MI DB auth active (:337-338 SQL admin login now dormant fallback); ⚠ :341 public network access (accepted)
18 UI Architecture & Components 3 4 8 12/24 ↓ Implementation 9→8 (twenty-second cycle): one isolated, citable gap takes it out of the exemplary band. Maturity holds at 4, re-confirmed independently: UIArchitectureConventionTests.cs:12 (a sealed subclass of the shared UIArchitectureConventionTestsBase) machine-enforces the container/presentational split (code-behind presence and inline @code caps on routable pages), the arch-test project is in MMCA.ADC.CI.slnf:58, and that filter is built and tested by the build-and-test job (deploy.yml:181), a required check on every PR (CONTRIBUTING.md:80), meeting the rubric's automatically-enforced bar (../MMCA.Common/ArchitectureEvaluationCriteria.md:29). The split itself is still genuinely disciplined (routable @page components carry .razor + .razor.cs code-behinds; no business logic in markup, data via injected UI services), with typed parameter/EventCallback contracts using EditorRequired, composition via RenderFragment, and heavy reuse of a shared Common.UI base class plus primitives (none of the blob/prop-drilling red flags). What drops it to 8: the largest code-behind in the repo sits at exactly 400 lines against the enforced 400-line cap, zero headroom, with six more files in the 360-379 band (SessionSelectionDashboard 379, SessionDetail 376, PublicSessionList 367, SpeakerDetail 365, EventDetail 365, SessionLive 362), so the next added method fails the gate rather than being caught in review. Tracked as TD-16 (RemediationBacklog.md), resolution path is presentational sub-component extraction per the TD-13 pattern. The prior 9 also rested partly on a citation to lines that do not exist (MobileInfiniteScrollList.razor:49-52; the file is 43 lines). Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/UIArchitectureConventionTests.cs:12 (the §18 gate); MMCA.ADC.CI.slnf:58 + deploy.yml:181 (gates every PR); Common UIArchitectureConventionTestsBase.cs:22 (MaxCodeBehindLines => 400, MaxInlineCodeLines => 120); Engagement.UI/Pages/HappeningNow/HappeningNow.razor.cs:400 (flush at the cap); Common DataGridListPageBase.cs:18 (shared abstract base); Conference.UI/Pages/Event/EventList.razor.cs:16,21 (@inherits base, [Inject] IEventUIService, no data access)
19 State Management & Data Flow 3 4 9 12/27 ↑ Maturity 3→4 (twentieth cycle): the §19 fitness gate whose absence drove the fifteenth-cycle D12 recalibration now exists and gates CI. StateManagementConventionTests.cs:11 (a sealed subclass of the shared StateManagementConventionTestsBase, shipped in the pinned v1.116.0 package) machine-enforces the two §19 rules: UI assemblies carry no mutable static state (base :27; a static member is shared across every Blazor Server circuit) and stateful *StateService/*StateContainer services stay scoped, never singleton (base :65); the ADC subclass declares no exemptions, and the arch-test project runs in the merge/deploy gate (MMCA.ADC.CI.slnf:56), so the per-circuit ownership model is Optimized (4), the same rationale as §18. Implementation holds at 9 (a first-pass 9→8 proposal was adversarially rejected as an unsupported downgrade; no new §19 red flag exists): single-source-of-truth state with explicit ownership (component-local vs per-circuit scoped service), zero mutable static user/session fields (now machine-proven, not just swept), unidirectional flow with immutable records, StateHasChanged/InvokeAsync used intentionally, the stale-IsDirty red flag pre-empted via IsDirtyAccessor; PersistentComponentState avoids per-render refetch; the disposed-CTS race is tolerated. Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/StateManagementConventionTests.cs:11 (the §19 gate); Common StateManagementConventionTestsBase.cs:27,65 (the two rules); MMCA.ADC.CI.slnf:56; Common ListPageStateService.cs:9,81 (per-circuit scoped, immutable record with); Common DependencyInjection.cs:55 (TryAddScoped); UnsavedChangesGuard.razor:32; DataGridListPageBase.cs:538-560
20 Design System & UI Consistency 2 4 9 8/18 ↑ from impl 8. MudBlazor used consistently with a single centralized theme (palette/typography/spacing from one MMCATheme + CSS-variable tokens kept in sync by a test), and dark mode is now functionally wired (Common MMCA.Common.UI/Layout/MainLayout.razor:17,40 bind IsDarkMode + the ThemeToggle; Theme/MMCATheme.cs:48 supplies PaletteDark), so Day/Dark is a real capability, not just a token surface. The ADC landing page is now brand-token-clean: ADCHome.razor.css:215,252,277 in both UI hosts use var(--mmca-primary), guarded by Tests/Architecture/MMCA.ADC.Architecture.Tests/BrandColorTokenTests.cs:26-37. A borderline 8→9 call: the two residual deductions persist but are Common-side (BrandColorTokenTests guards Primary only, Secondary has no drift test; a few !important overrides + Store-specific cart CSS live in Common's shared app.css), and dark mode is now a functional capability. — Common MMCATheme.cs:24 (Secondary #00796B for WCAG 5.3:1), :48 (PaletteDark); MMCA.Common.UI/Layout/MainLayout.razor:17,40 (IsDarkMode + ThemeToggle); ADCHome.razor.css:215,252,277 (both hosts, var(--mmca-primary)); BrandColorTokenTests.cs:26-37; DataGridListPageBase.cs:328-388 (RowsPerPage/CurrentPage v9 guardrail, BL0005 justified); ⚠ Common app.css:116-148 (Store cart !important, shared-Common, minor)
21 Accessibility (a11y) 3 3 8 9/24 ↑ Maturity 2→3 + Implementation 7→8 (fifteenth cycle): the axe layer is now an enforced deploy gate. Implementation is strong (stated WCAG 2.1 AA target, real semantic landmarks/skip-nav/focus/aria-labels, native controls, contrast tuned, axe-core in shared E2E bases ADC consumes, scans broadened to 17 pages), and the chromium E2E/axe suite now blocks every production deploy (e2e-gate in deploy.needs, promoted 2026-07-02; validation run 28604877733 was fully green, satisfying the former "impl 8 pending a green run" condition). Maturity stops at 3, not 4: the rubric's §21 verification criterion pairs automated CI checks with periodic manual screen-reader/keyboard passes, and ACCESSIBILITY-SCREENREADER-PASS.md still awaits a recorded dated pass. Evidence: Common MainLayout.razor:18,21,29,67,79 (skip-nav, role landmarks, role=alert); EventList.razor:12,55,125 (aria-label icon buttons; 64 aria attributes across 18 ADC .razor files); Tests/E2E/MMCA.ADC.E2E.Tests/Workflows/AccessibilityTests.cs (17 pages); deploy.yml:344 (e2e-gate in deploy.needs); ACCESSIBILITY-SCREENREADER-PASS.md (runbook ready, dated pass pending, the maturity-4 lever)
22 Responsive & Cross-Browser 2 3 8 6/16 ↓ Maturity 4→3 (twenty-second cycle): the deploy gate was cut to a single engine, so cross-browser verification is no longer enforced. The twenty-first cycle scored M4 on a deploy-gating e2e-gate that passed chromium + firefox + webkit; the 2026-07-18 Actions-minute reduction narrowed it to browsers: '["chromium"]' (deploy.yml:488, rationale recorded in the job comment at :478-480, job still in deploy.needs at :783). Firefox and webkit now run only on the weeknight nightly matrix (e2e.yml:39 cron 0 7 * * 1-5, further skipped when the branch has not advanced, :85-95), and on that scheduled event they are continue-on-error (e2e.yml:119). Cross-engine coverage is therefore nightly-advisory, enforced by convention rather than automatically, which is the rubric's Consistent (3), not Optimized (4). This is a deliberate cost trade-off, recorded in the backlog's Deliberate/accepted section, not a regression in the responsive work. Implementation holds 8 on unchanged substance: fluid layouts via MudBlazor grid/breakpoints, data grids degrading to card lists on mobile (no horizontal-scroll/unusable-grid red flag), the shared .mmca-touch-target 48px affordance (Common, v1.94.0), and a documented Chromium/Firefox/WebKit matrix that still runs, just off the gate. Maturity 4 lever: re-add the two legs to the deploy gate, or add a cross-browser-freshness job to deploy.needs mirroring the dr/load/cross-service freshness pattern (deploy.yml:496,553,610) so a stale or red nightly matrix blocks the deploy at near-zero runner cost. Evidence: deploy.yml:478-480,488,783; e2e.yml:39,113,119; Common BreakpointConstants.cs:16-17 (<960px drives grid→card); EventList.razor:28-60 (MobileInfiniteScrollList) + EventCreate.razor:39-56 (MudGrid xs/sm); Common app.css:195-200 (hide-below-desktop columns)
23 Front-End Performance 2 4 8 8/16 ↑ Maturity 3→4 (twentieth cycle): the Core Web Vitals budgets are now enforced, not advisory. Server-side paging/filtering/sorting (no load-everything-then-page-in-memory), bounded virtualized infinite scroll with @key, debounced search, request cancellation, SSR prerender + PersistentComponentState handoff, and output caching all present: strong client-side hygiene. The nineteenth cycle correctly rejected a maturity lift because the vitals budgets were advisory by design; on 2026-07-11 they were recalibrated into real failing assertions (WebVitalsTests.cs:76 hard-asserts LCP/FCP/TTFB/CLS budgets, :17 the recalibration) and the suite runs unfiltered inside the deploy-gating chromium e2e-gate (e2e.yml:236 runs the whole E2E project; deploy.yml:417 e2e-gate in deploy.needs), so a red vitals assertion blocks the production deploy: the same deploy-gate = M4 standard already applied to §28/§29/§31. It is a deploy gate, not a PR merge gate (deploy.yml:306 excludes pull_request), which meets the rubric's automatically-enforced bar (../MMCA.Common/ArchitectureEvaluationCriteria.md:29). Implementation holds at 8. Evidence: Common EntityServiceBase.cs:51 (server-side paging/sort/filter via query params); DataGridListPageBase.cs:403-496 (CTS-cancelled ServerData + prerender-bounded fetch, :116-138,411-428 persist/restore); MobileInfiniteScrollList.razor:16,61,165-167
24 Forms, Validation & UX Safety 2 4 8 8/16 ↑ Implementation 7→8 (twentieth cycle): TD-14 shipped, closing the sixteenth-cycle recalibration's sole systematic gap; Maturity holds 4 on the extended CI-gated fitness function. Client validation mirrors server FluentValidation rules, the unsaved-changes guard reads current dirty state via a live accessor (avoids the stale-IsDirty red flag), submit is disabled while saving (no double-submit), _isDirty clears before NavigateTo on success (no false prompt), and destructive actions are confirmed via MudMessageBox. The former deduction is closed: all six Conference create forms now render the per-form MudAlert error summary with a localized heading (EventCreate.razor:108 @if (_form is { Errors.Length: > 0 }) + Validation.CorrectFollowing), and the gate enforces it: FormsConventionTests.cs:18 extends RequiredMarkers with the MudAlert Severity="Severity.Error" + Validation.CorrectFollowing markers across MinimumCreateForms >= 6, and the former Profile-form exclusion is covered by the dedicated ProfileForm_KeepsErrorSummaryAndPasswordValidation fact (FormsConventionTests.cs:32), all in the CI.slnf arch gate (MMCA.ADC.CI.slnf:56). The formerly-raw exception snackbars stay localized whole-sentence keys (Profile.razor.cs:235 Snackbar.ChangePasswordFailed, no {ex.Message} interpolation). Held at 8: the top-of-Strong score reflects that the summary/marker conventions are string-marker enforced rather than semantically asserted (a bUnit render-level assertion of the summary's error items is the 8→9 lever). Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/FormsConventionTests.cs:18,32; Common FormsConventionTestsBase.cs:29; MMCA.ADC.CI.slnf:56; EventCreate.razor:8,108 (guard + per-form summary); EventCreate.razor.cs:57,89,125 (client validation before submit; _isDirty=false before NavigateTo; Disabled while IsSaving); Profile.razor.cs:235,251 (localized snackbar keys; MudMessageBox delete confirm); Conference.Application/Events/UseCases/Create/EventCreateRequestValidator.cs:9 (server-side parity)
25 Navigation & Information Arch 2 4 8 8/16 All criteria met and enforced: clean bookmarkable/typed routes, route-level [Authorize] guards (server-enforced via SSR cookie auth, not just UI hiding), role-aware menu filtering, documented per-actor NavigationFlow.md matching code, 404 (NotFoundPage) and 403 (dedicated <Forbidden/> page, Routes.razor:26) handled in the shell; bUnit route-authorization tests (per-PR gated via MMCA.ADC.CI.slnf) prevent role-guard regression. Impl below 10 only because some auth pages still require client-side nav (the prior "generic 403 alert" deduction is closed: a dedicated <Forbidden/> 403 page now renders in the shell). — Common Routes.razor:6-31; Common NavMenu.razor:151-159 (RequiredRole/RequiredClaim filtering); EventCreate.razor:2 ([Authorize(Roles="Organizer")]); 21 admin pages identically gated + Conference.UI.Tests/ManagementRouteAuthorizationTests.cs + Identity.UI.Tests/IdentityRouteAuthorizationTests.cs (bUnit tier)
26 Front-End Security 3 4 9 12/27 Reference-quality client security: no unsanitized HTML/MarkupString, tokens in HttpOnly cookies with the refresh token unreadable by JS and no localStorage, an enforced CSP with locked script-src/connect-src plus security headers/antiforgery, no secrets in the WASM bundle (runtime config fetch), authorization re-enforced server-side. Held at 9 only because img-src https: and style-src 'unsafe-inline' are deliberately permissive (documented, low-risk). — Hosts/UI/MMCA.ADC.UI.Web/Program.cs:55-58 (AddCommonBlazorCsp() / AddCommonSecurityHeaders() / AddSessionCookieAuthentication(): the CSP, token-storage, and security-headers mechanism now lives in MMCA.Common and is consumed here; HttpOnly refresh cookie never JS-readable, no localStorage, locked script/connect-src), :86-87,92,113,118 (UseAntiforgery + UseAuthenticatedNoStore + enforced CSP)
27 Internationalization (i18n) 1 4 8 4/8 ↑ Implementation 7→8 (seventeenth-cycle i18n completion sweep, ADR-027 Decision 9); Maturity 4 holds and gains a second gate. ADC ships real en-US + es localization: 40 base .resx + 40 .es.resx (every base has an .es sibling; covers the module UIs, both ADCHome hosts, the nav items, and the API error-resource sets), IStringLocalizer<T> across every page. The fourteenth-cycle deductions are ALL closed by the sweep: zero hard-coded snackbars (whole-sentence page keys; raw {ex.Message} never surfaces), zero literal page titles (Store's Title => L["Title"].Value pattern adopted on all ~24 pages), zero literal breadcrumbs (built in OnInitialized from Breadcrumb.* keys), the ErrorMessages.Success concatenation red flag is gone (obsoleted in Common, all 28 call sites swept), nav menus localize via NavItem.TitleResource, and MudBlazor built-in chrome now localizes via the framework's ResxMudLocalizer (inherited). Culture alignment/persistence unchanged (UI.Web/Program.cs:111, UI.Web.Client/Program.cs:71, User.PreferredCulture). Maturity 4 (Optimized), now doubly gated in CI.slnf: TranslationCompletenessTests.cs:12 (RequiredCultures=[es], floor raised 30→40) + the NEW LocalizedTextConventionTests.cs (no hard-coded snackbar/title/<PageTitle>/breadcrumb/NavItem literal can ship; MinimumScannedFiles=60). Held at 8 (twenty-first-cycle adjudication: a proposed impl 8→9 was adversarially rejected): the wave-6 PseudoLocalizationTests tier (2026-07-11) extends the pseudo-loc no-overflow (text-expansion) evidence to ADC pages, but only 3 public pages (/, /conference/events, /conference/sessions, PseudoLocalizationTests.cs:51) of the 30+ routable pages, a partial extension that does not clear the exemplary band; broadening it across the authenticated surfaces is the 8→9 lever. Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/{TranslationCompletenessTests.cs:12,LocalizedTextConventionTests.cs}; EventCreate.razor.cs (Title/breadcrumb/snackbar keys), Profile.razor.cs (sanitized snackbars), ConferenceUIModule.cs (TitleResource + resx pair); Common ErrorMessages.cs ([Obsolete] Success), ResxMudLocalizer.cs; verification: ADC CI suite 1279/1279 green post-sweep
28 Front-End Testing & Quality 3 4 8 12/24 ↑ Maturity 3→4 (fifteenth cycle): the E2E/axe layer is now an enforced deploy gate. Strong shape: bUnit component + route-auth tests gate every PR (CI.slnf via deploy.yml), a broad Playwright suite with shared page objects/abstract bases (no per-app duplication) covers role journeys, robust role/regex selectors avoid sleep-flakiness, axe runs in the shared Login/Register/Profile bases ADC consumes. The chromium E2E suite was promoted to a deploy gate on 2026-07-02 (e2e.yml:9 workflow_call + e2e-gate in deploy.needs) after validation run 28604877733 returned the first fully green three-browser matrix (chromium 99 tests / 0 failed; firefox/webkit green, advisory via e2e.yml:74 continue-on-error). Twentieth-cycle correction: the prior row's "E2E #5 is un-skipped/active" claim was false as written; the test is re-quarantined (SpeakerSelfServiceTests.cs:57 carries an active [Fact(Skip = "E2E-WASM: ...")] over a stale SessionsCache read on the speaker dashboard, a different cause than the original GetAll populator bug, which stays fixed and guarded by the active SessionIncludeChildrenRegressionTests.cs:27). The create-form bUnit test (EventCreateTests.cs:17, blank-form validation gating + cancel navigation) and the SessionSpeakers regression test still narrow the state-coverage gap; the scores hold on the deploy-gate + pyramid evidence, not on the corrected claim. Evidence: deploy.yml:417 (needs incl. e2e-gate), :305 (e2e-gate job, three browsers, non-PR); e2e.yml:9,74; MMCA.ADC.CI.slnf:43,49,55 (three UI.Tests bUnit projects per PR); EventCreateTests.cs:17; SessionIncludeChildrenRegressionTests.cs:27
29 Resilience & Business Continuity 3 4 9 12/27 ↑ maturity 3→4 (this wave): a DR-freshness gate now makes the recovery proof block the deploy (impl held at 9). Failure isolation (Polly retry/breaker as a fitness-enforced framework invariant), graceful degradation (outbox), geo-redundant PITR+LTR backups, defined RTO/RPO, SLO alerts, and smoke-gate auto-rollback are all present — and the rubric's #1 red flag stays closed: the restore has been drilled (DISASTER-RECOVERY.md:140-144 — 2026-06-20 PITR restore of ADC_Conference in 2.6 min vs 2 h RTO, automated via dr-drill.yml; TD-10 closed). Graceful shutdown is genuinely CI-verified: the paired fault-injection test landed in Common (ResilienceCircuitBreakerFaultInjectionTests, consumed), and GracefulShutdownTests boots the real Gateway via WebApplicationFactory, requests a stop under a bounded ~20s token, and asserts the host drains (ApplicationStoppingApplicationStopped) within the timeout (headless, in CI.slnf), so Implementation holds at 9. Maturity is now 4: a dr-freshness job added to deploy.yml's deploy.needs fails the deploy if the latest dr-drill.yml run did not conclude success within an 8-day freshness window (covering the weekly cron), so the recovery proof now blocks the deploy and the rubric's M4 ("enforced automatically by a CI gate") is met by the freshness gate rather than the scheduled-but-non-gating cron alone. Right-sized for the ~67-peak load: the gate reads the last drill's status via one Actions API call with no per-deploy restore cost, while the real PITR restore still runs on dr-drill.yml's weekly cron (the gate is committed and activates on the next push). The Azure Monitor DR dashboard ships (sloWorkbook in main.bicep:278, embeds workbooks/adc-slo-workbook.json). — ADR-009 (RTO/RPO + single-region acceptance); .github/workflows/deploy.yml (dr-freshness job in deploy.needs, the 8-day recovery-proof gate); .github/workflows/dr-drill.yml:27-29 (weekly cron, the real PITR restore the gate reads); Tests/Hosts/MMCA.ADC.Gateway.Tests/GracefulShutdownTests.cs:33-39; infra/main.bicep:431 (LTR on all 4 ADC_* DBs), :235 (3 SLO metricAlerts), :278 (SLO workbook)
30 Compliance, Privacy & Governance 2 4 9 8/18 ↑ from impl 8. Reconciles soft-delete with right-to-erasure exactly as the rubric demands: real anonymize-in-place path, export/access endpoint, bounded outbox/log retention, documented PII inventory, and a PII fitness function enforcing the erasure obligation in CI. The residency contradiction is resolved: PRIVACY.md:44,52 now states East US 2 apps / West US 2 SQL (matching the deployment), guarded by a new DataResidencyTests fitness function so docs and main.bicep cannot drift again. — ADR-005; Identity.Domain/Users/User.cs:16,322-337 (User : IAnonymizable; idempotent Anonymize in place); DeleteUser/DeleteUserHandler.cs:47-49; Tests/Architecture/.../DataResidencyTests.cs
31 Cost Efficiency / FinOps 2 4 8 8/16 ↑ maturity 3→4 (this wave): the surge-drift guard is now a deploy gate, not just a weekly cron. FinOps is a first-class, data-driven axis: right-sized Basic tiers matched to a measured ~67-peak load, an automated revert-drift guard for temporary surges, a budget with spend alerts, tuned log retention + telemetry filtering, per-component cost tags — directly answering every criterion/red flag. Maturity is now 4: cost-guard.yml is a workflow_call reusable workflow invoked as a cost-guard job in deploy.yml's deploy.needs, so a deploy is blocked while a conference-day surge is un-reverted (committed, activates on the next push) rather than only flagged weekly. Impl held at 8: the scale-up revert is still guard-and-notify rather than auto-reverting, and surge itself is a manual action. — .github/workflows/cost-guard.yml (read-only surge-drift check, now also workflow_call: fails if Container App maxReplicas>2 or any SQL DB tier!=Basic); .github/workflows/deploy.yml (cost-guard job in deploy.needs); infra/main.bicep:299-326 (monthly RG budget $200, 80%/100% alerts); load-test.yml:13 (k6 67 VUs = observed peak)
32 Dependency & Supply-Chain 2 4 9 8/18 ↑ impl 8→9 this cycle (the --locked-mode lever is already pulled); maturity 4 unchanged (TD-01 closed earlier). Central pinned versions, a deliberately licence-pinned MassTransit v8 (the exact 'accidental major bump reintroduces known-bad' red flag, defended with rationale), coordinated all-consumers-at-once upgrade — now with committed lock files and blocking supply-chain CI gates. ADC committed 58 packages.lock.json (RestorePackagesWithLockFile=true), and its vuln-audit + SBOM steps are now blocking PR gates (in deploy's needs, run on pull_request), so transitive provenance is frozen and supply-chain governance is CI-enforced (maturity → Optimized). Implementation moves to 9 this cycle because the scorecard's own stated 8→9 lever is already satisfied: CI restore runs --locked-mode in both gating jobs (deploy.yml:40 build-and-test, :119 supply-chain), so lock-file drift is now tamper-enforced at restore. Held below 10 only because MassTransit v8 is pinned only transitively (inherited from Common, not in ADC's own props). — Directory.Build.props:27 (RestorePackagesWithLockFile); 58 committed packages.lock.json (commit 6248273); deploy.yml:129-148 (audit gate, :146 exit 1, no continue-on-error), :150-155 (SBOM gate, :155 exit 1), :344 (deploy needs [...supply-chain, cost-guard, dr-freshness, e2e-gate]); Directory.Packages.props:121-139 (MMCA.Common.* all pinned 1.117.0, lockstep); nuget.config:8-19 (auditSources clears→nuget.org, packageSourceMapping MMCA.*→github); ✓ --locked-mode on CI restore in both gating jobs (deploy.yml:40, :119); ⚠ MassTransit v8 pinned only transitively via Common, not in ADC's own props
33 Developer Experience & Inner Loop 2 3 8 6/16 Held at 3/8 (twentieth cycle): a proposed impl 8→9 was adversarially rejected. Inner loop hits nearly every criterion: genuine one-command Aspire orchestration, a documented gitignored local.props cross-repo override, consistent analyzer/editorconfig tooling shared with CI, a fast CI solution filter, and the former README-stub friction is resolved (README.md:1 is now a full getting-started guide: prereqs, GITHUB_TOKEN/local.props bootstrap, run/test commands). The remaining friction keeps it in the Strong band: broker parity is mitigated, not closed. Local runs RabbitMQ while prod runs Azure Service Bus (both via MassTransit), and the README's own parity notes admit "Service-Bus-specific behavior is only observable in the deployed environment" (README.md:74), which matches the rubric's local-diverges-from-prod red flag (../MMCA.Common/ArchitectureEvaluationCriteria.md:838), so impl 9 requires closing (not documenting) that gap. Evidence: README.md:1,74; CLAUDE.md (one-command dotnet run --project Source/Hosting/MMCA.ADC.AppHost → SQL/Redis/RabbitMQ/MailDev + 4 services + Gateway + UI); AppHost/Program.cs:224-282 (health-based WaitFor ordering, deliberate no-WaitFor reverse gRPC edge); local.props.template:5 (UseLocalMMCA)
34 Architecture Governance & Docs 2 4 9 8/18 Reference-quality governance: a maintained ADR set (001-048, canonical in MMCA.Common) for every non-obvious decision, executable fitness functions (26 architecture-test classes (28 .cs files; 82 executed methods) gating CI, incl. resilience/PII/concurrency/microservice-extraction/data-residency/cross-source-specification/UI-architecture/state-management/observability-pairing, 79 methods thin subclasses of the shared MMCA.Common.Testing.Architecture rule library plus the 3 ADC-local §13 pairing-gate methods, ADR-015) rather than prose, a living architecture map + scored backlog, and documented conventions linking to the enforcing tests. The arch-test doc-comment caveat is fixed this cycle (SpecificationConventionTests.cs:3-7 now frames the reverted polyglot layout as a forward safeguard). Governance improved this cycle: this ArchitectureScorecard.md is now the canonical two-axis evaluation, versioned in-repo (the rubric + framework facts moved to MMCA.Common, mirroring the ADR §34 pattern), and the prior ledger-lag is resolved (RemediationBacklog.md marks TD-01/TD-05 (#32/#14) done). The backlog is the single remediation + tech-debt ledger (the former TECHDEBT.md TD-NN register was folded into it 2026-06-26, matching Common/Store). Caveat (minor): ArchitecturalAnalysis.md still lives at the untracked workspace root. Evidence: ADRs/001-048 (canonical in MMCA.Common, indexed in ADRs/README.md); Tests/Architecture/MMCA.ADC.Architecture.Tests/ (28 .cs files: 26 test classes + map + global-usings); this ArchitectureScorecard.md (canonical two-axis) + RemediationBacklog.md (single ledger; TD-NN folded in: done TD-01/03/04/05/09/10, open TD-02/06/07/08) + workspace ArchitecturalAnalysis.md

Weighted column = Maturity·weight / Implementation·weight per row. Axis-gap findings: the former §21 gap (excellent-but-not-enforced, impl 7 over maturity 2) closed on 2026-07-02 when the chromium E2E/axe suite became a deploy gate (§21 now M3/I8; the recorded screen-reader pass is the remaining maturity-4 lever). The 2026-06-30 enforcement-gate wave lifted §16/§24/§27/§29/§31 maturity 3→4, making each category's already-strong implementation enforced by a CI gate (three new fitness tests in the CI.slnf arch gate, plus the cost-guard and dr-freshness deploy gates), which closed most of the Maturity-vs-Implementation inversion. The prior 2026-06-29 re-score had corrected §29 maturity 4→3 (the DR restore drill was then a scheduled-but-non-gating cron; this wave's dr-freshness deploy gate now makes it an actual gate, so §29 is back to maturity 4 on substantively different evidence) and lifted §32 implementation 8→9 (CI restore runs --locked-mode in both gating jobs); §5/§7/§13/§25 were adversarially FLAG-re-checked and confirmed unchanged. The subsequent v1.93.0 sweep (2026-06-30) then lifted §5 Vertical Slice Architecture maturity 3→4 (the slice-cohesion fitness function SliceCohesionTests is confirmed a CI merge gate in MMCA.ADC.CI.slnf), and re-confirmed §7 at M4/I8 (a proposed impl 8→9 lift adversarially rejected over the bidirectional Conference↔Engagement gRPC pair). Earlier waves had flipped §27 i18n from N/A to scored (M3/I8), moved §20/§24 implementation 8→9, lifted §29 impl 8→9 (graceful-shutdown failure test CI-gated), closed §32 mature-but-not-locked (lock files committed, M3→4/I7→8), §29 recovery (drilled), and §30 (residency matched). The sixteenth-cycle full re-score (2026-07-03) recalibrated §24 Implementation 9→7 (the per-form error summary exists only on the Profile form, the six create forms surface a generic validation snackbar, and the Profile handlers show raw exception text), the only score move of that cycle; §24 Maturity holds 4 on the FormsConventionTests gate and the residual is tracked as TD-14. The eighteenth (2026-07-06, pin v1.106.0) and nineteenth (2026-07-10, pin v1.110.0) full re-scores moved no score; the nineteenth adversarially rejected three proposed moves (§12 impl 8→9 on the Notification single-replica pin, §23 maturity 3→4 on the advisory-by-design vitals budgets, §34 impl 9→8 as unsupported), each a verified non-move. The twentieth-cycle full re-score (2026-07-15, pin v1.116.0) lifted §18/§19/§23 maturity 3→4 (the §18/§19 fitness gates now exist in the CI.slnf arch gate; the §23 vitals budgets became enforced assertions inside the deploy-gating e2e-gate on 2026-07-11) and §13/§24 implementation (8→9 on the shipped SLO workbook + OPERATIONS.md runbook; 7→8 on TD-14's per-form error summaries), while re-rejecting the §34 9→8 downgrade, rejecting a §33 impl 8→9 on the open broker-parity red flag, and correcting §28's false "E2E #5 un-skipped" claim in place (score held). This closes the §18/§19 half of the Maturity-vs-Implementation inversion; the below-maturity-4 set narrows to §12/§13/§21/§22/§33. The twenty-first-cycle full re-score (2026-07-17, pin v1.117.0) lifted §13 and §22 maturity 3→4 (the ADC-local alert-runbook pairing fitness gate and the fully gating three-browser e2e-gate, both shipped 2026-07-16), adversarially rejected the §27 impl 8→9 pseudo-loc candidacy (3 pages of 30+, partial), re-confirmed §12 at M3/I8, and corrected the stale nineteenth-cycle draft prose that PR #15 accidentally committed; the below-maturity-4 set narrows to §12/§21/§33. The twenty-second-cycle full re-score (2026-07-21, pin v1.121.0) moved two scores down, neither on a quality regression: §22 maturity 4→3 (the 2026-07-18 CI-minute reduction cut the deploy e2e-gate to chromium only, deploy.yml:488, leaving firefox/webkit nightly-advisory, e2e.yml:119, a deliberate cost trade-off now recorded as such) and §18 implementation 9→8 (the largest code-behind is flush at the enforced 400-line cap with zero headroom, TD-16), while re-rejecting the §27 impl 8→9 pseudo-loc candidacy a second time on unchanged evidence. The below-maturity-4 set widens to §12/§21/§22/§33, and this reopens the Maturity-vs-Implementation picture on the front-end operational side: the §22 gap is not "the tests do not exist" but "the tests no longer gate."

Indices

  • Maturity index = Σ(maturity×weight) ÷ Σ(weight×4) = 311 ÷ 320 = 97.2% (down from 97.8%, 313/320: the twenty-second-cycle full re-score (2026-07-21, pin v1.121.0) corrected §22 4→3 (-2 weighted) after the 2026-07-18 Actions-minute reduction cut the deploy e2e-gate to chromium only (deploy.yml:488), leaving firefox/webkit on the nightly matrix where they are continue-on-error (e2e.yml:119), so cross-engine verification is no longer automatically enforced. The open maturity-3 set widens to §12/§21/§22/§33. The twenty-first-cycle §13 3→4 lift and the twentieth-cycle §18/§19/§23 3→4 lifts stand.) Re-confirmed unchanged by the twenty-third-cycle full re-score (2026-07-23, pin v1.123.0, no moves; the §12 and §21 maturity 3→4 proposals were adversarially rejected).
  • Implementation index = Σ(impl×weight) ÷ Σ(weight×10) = 687 ÷ 800 = 85.9% (down from 86.3%, 690/800: §18 impl 9→8 (-3 weighted) on the code-behind flush at the enforced 400-line cap with six more files in the 360-379 band, TD-16. The §27 impl 8→9 pseudo-loc candidacy was adversarially rejected for a second consecutive cycle on unchanged evidence, 3 public pages of 36 routable. The twentieth-cycle §13 8→9 and §24 7→8 lifts, the seventeenth-cycle §27 7→8 lift, and the fourteenth-cycle §6 10→9 correction stand.) Re-confirmed unchanged by the twenty-third-cycle full re-score (2026-07-23, pin v1.123.0, no moves; every implementation score is 8 or higher).
  • Weaker axis: Implementation (execution quality), by ~11.3 points
  • N/A (excluded from denominators): none: §27 Internationalization is scored (M4/I8) as of ADR-027 (which supersedes the single-locale ADR-011), so no category is excluded from the denominators.
  • §32 weight = 2 (the default; raised to 3 only for the published framework MMCA.Common)

Top 5 strengths

  1. Architecture intent is executable, not prose: 25 architecture-test classes / 79 methods gate CI (§34 Governance, mat 4 / impl 9, and §3 Clean Architecture, mat 4 / impl 9): Tests/Architecture/MMCA.ADC.Architecture.Tests/ with LayerDependencyTests, DomainPurityTests, MicroserviceExtractionTests (thin subclasses of the shared rule library), plus PiiConvention/Concurrency/IntegrationEventContract/Specification/SliceCohesion/DataResidency/ConstructorDependencyCount/BrandColorToken/UIArchitectureConvention/StateManagementConvention: 79 methods inherited from the shared MMCA.Common.Testing.Architecture bases (ADR-015) via 25 thin subclasses (0 ADC-local methods; the two newest, the §18/§19 gates, closed the last review-enforced-only UI conventions this cycle); ADRs 001-048 (canonical in MMCA.Common) capture the 'why'.

    • Remediation: Resolve the provenance nit — ArchitecturalAnalysis.md still lives at the untracked workspace root outside all three git repos (the arch-test doc-comment drift was already fixed in SpecificationConventionTests.cs).
    • Expected delta: Tracking the governance docs in-repo lifts §34 impl 9→10.
  2. Disaster recovery is drilled, graceful shutdown is CI-verified, and the privacy residency claim matches the deployment: §29 (mat 4 / impl 9) and §30 (mat 4 / impl 9): DISASTER-RECOVERY.md:140-144 records a 2026-06-20 PITR restore (2.6 min vs 2 h RTO, TD-10 closed); Tests/Hosts/MMCA.ADC.Gateway.Tests/GracefulShutdownTests.cs:33-39 CI-verifies the host drains within a bounded timeout (impl lifted 8→9 in an earlier wave); PRIVACY.md:44,52 reads East US 2 / West US 2 and is guarded by DataResidencyTests. The two single-biggest §29/§30 red flags from the prior report are eliminated.

    • Remediation: Resolved this wave: a dr-freshness job in deploy.yml's deploy.needs fails the deploy unless the latest dr-drill.yml run concluded success within an 8-day window, so the recovery proof now gates the deploy (committed, activates on the next push). The remaining polish is a recorded restore-RTO trend.
    • Expected delta: Done this wave: the DR-freshness gate lifts §29 maturity 3→4.
  3. Reference-quality front-end security and UI state management: §26 (mat 4 / impl 9): Hosts/UI/MMCA.ADC.UI.Web/Program.cs:55-58 (AddCommonBlazorCsp()/AddCommonSecurityHeaders()/AddSessionCookieAuthentication(), all now consumed from MMCA.Common: HttpOnly-cookie tokens, refresh unreadable by JS, no localStorage, locked script-src/connect-src, frame-ancestors none); §19 (mat 4 / impl 9): ListPageStateService.cs:9,81 (per-circuit scoped, immutable records), UnsavedChangesGuard.razor:32 (IsDirtyAccessor pre-empts the stale-IsDirty red flag); the zero-mutable-static-cross-user-state property is now machine-proven by the CI-gating StateManagementConventionTests (twentieth cycle), not just swept.

    • Remediation: Tighten the two deliberately-permissive CSP directives: replace style-src 'unsafe-inline' with hashed/nonce styles and narrow img-src https: to specific origins. (The formerly-recommended §19 fitness test shipped this cycle.)
    • Expected delta: Hardening the two CSP directives lifts §26 impl 9→10.
  4. Capacity and FinOps are data-driven from measured conference load: §31 (mat 4 / impl 8) and §12 (mat 3 / impl 8): load-test.yml:13 PEAK_VUS=67 = observed 2026 peak; conference-read-load.js:35-36 (p95<800ms, <1% errors); the k6 capacity proof's recency gates the deploy via the load-freshness job (deploy.yml:348,417), though the tier itself still runs monthly/dispatch (§12 holds M3, re-confirmed this cycle; a stale PR-#15 hunk claiming mat 4 is corrected here); cost-guard.yml (weekly read-only drift check fails if maxReplicas>2 or any SQL DB tier!=Basic); main.bicep:317 ($200 monthly RG budget with 80%/100% alerts); new this cycle: a protective 1 GB/day Log Analytics ingestion cap (foundation.bicep:41).

    • Remediation: Convert the conference-day surge/revert from a manual play with a drift alarm into a scheduled, automated scale event with an automatic revert.
    • Expected delta: Automating the surge/revert lifts §31 impl 8→9.
  5. Non-inverted test pyramid with a deploy-gated integration tier AND a deploy-gated chromium E2E/axe suite: §14 (mat 4 / impl 9) and §28 (mat 4 / impl 8): 1507 unit/UI Fact/Theory across 223 files; 79 architecture-test methods across 25 test classes; 303 gating integration methods across four per-service WAF tiers; the integration tier, a 55.5% unit-tier coverage floor, cost-guard, dr-freshness, and the chromium E2E gate all block deploy (deploy.yml:417,:83). E2E #5 is re-quarantined (SpeakerSelfServiceTests.cs:57, stale SessionsCache read on the speaker dashboard; a corrected claim this cycle) while the underlying split-query fix stays guarded by the active SessionIncludeChildrenRegressionTests.

    • Remediation: Record the manual screen-reader pass (§21 maturity 4 lever). The firefox/webkit gating half regressed on 2026-07-18: the deploy gate now runs chromium only (deploy.yml:488), so §22 fell back to maturity 3 and needs either the two legs restored or a cross-browser-freshness gate.
    • Expected delta: SR pass lifts §21 maturity 3→4 (the last weight-3 maturity gap); a cross-browser freshness gate lifts §22 back to maturity 4.

Top 5 risks

  1. The manual screen-reader pass is still unrecorded (the last weight-3 maturity gap): §21 (mat 3 / impl 8, weight 3): the chromium E2E/axe suite blocks every deploy (e2e-gate in deploy.needs, promoted 2026-07-02), but cross-browser verification is no longer enforced (§22 fell back to mat 3 this cycle: the 2026-07-18 Actions-minute reduction cut the gate to chromium only, deploy.yml:488, so firefox/webkit run only on the nightly matrix where e2e.yml:119 keeps them continue-on-error). The latent Warning-outlined-alert AA-contrast pattern was fixed 2026-07-16 (all six sites, see backlog #21). The one residual: ACCESSIBILITY-SCREENREADER-PASS.md still awaits a recorded dated pass, which the rubric pairs with the automated CI checks (needs a human + NVDA/VoiceOver against the running Aspire app; cannot be done headless).

    • Remediation: Record the manual screen-reader pass.
    • Expected delta: SR pass lifts §21 maturity 3→4 (weight 3), leaving §12/§22/§33 below maturity 4.
  2. SQL data-plane: public network access remains (the credential flag is now closed) — §11 (mat 4 / impl 9, weight 3) and §17 (mat 4 / impl 9): managed-identity DB auth is now ACTIVE in production (2026-06-28, useManagedIdentitySql=true), so the shared SQL password is removed from every app and the runtime SQL admin login is no longer used (retained only as a dormant fallback). That closed the credential half of this risk and realized the §17 8→9 lift. The residual is purely network exposure: infra/main.bicep:341 publicNetworkAccess: 'Enabled' + :345-352 the AllowAzure 0.0.0.0 firewall rule, both deferred by design (no VNet; documented-accepted in infra/SQL-MANAGED-IDENTITY.md).

    • Remediation: Move the SQL data plane onto private endpoints (disable public network access, drop the 0.0.0.0 rule). This is the VNet + private-endpoint epic, which requires recreating the Container Apps environment, hence deferred.
    • Expected delta: The managed-identity switch is DONE (§17 impl 8→9 realized). Closing the remaining public-network-access flag would lift §11 impl 9→10.
  3. No lock files for the consumer apps: RESOLVED (TD-01 closed); §32 now at mat 4 / impl 9: ADC committed 58 packages.lock.json (RestorePackagesWithLockFile=true, commit 6248273), its vuln-audit + SBOM are blocking PR gates, and CI restore now runs --locked-mode in both gating jobs (deploy.yml:40, :119), so lock-file drift is tamper-enforced at restore (impl 8→9 this cycle). No longer a top risk; retained for the audit trail.

    • Residual: MassTransit v8 is still pinned only transitively (inherited from Common, not in ADC's own props). Optionally pin MassTransit directly in ADC's Directory.Packages.props.
    • Expected delta: A direct MassTransit pin would lift §32 impl 9→10.
  4. Notification scale-out still single-replica; the backend performance tier stays advisory: §12 (mat 3 / impl 8), §23 (mat 4 / impl 8): uniform async, server-side paging, output caching, a measured k6 backend load test, and a client-side WebVitals tier are all in place, and the v1.110.0 wave scaled the REST services to maxReplicas: 2 on a shared Azure Managed Redis (cross-replica idempotency + cache invalidation, main.bicep:500-508,781,881,981). The vitals half closed this cycle: the CWV budgets are enforced assertions inside the deploy-gating e2e-gate (§23 maturity 3→4). The Notification app stays pinned to maxReplicas: 1 (main.bicep:1113) even though the backplane key is injected (:1056) and Common auto-wires the StackExchange.Redis backplane on it (:507-508); the pin is a deliberate right-sizing at the measured ~67 peak, but hub scale-out remains unverified, and while the k6 proof's recency now gates the deploy (load-freshness, deploy.yml:348,417), the tier itself still executes monthly/dispatch out of band, keeping §12 at maturity 3 (adjudicated in the twentieth cycle and re-confirmed in the twenty-first).

    • Remediation: Verify a two-replica Notification fan-out against the wired backplane, then raise maxReplicas; for §12 maturity 4, move the capacity proof itself in-band (or to a gating cadence), not just its recency check.
    • Expected delta: A verified two-replica fan-out lifts §12 impl 8→9; an in-band capacity proof lifts §12 maturity 3→4.
  5. Concurrency and contract-evolution mechanisms are wired but thinly exercised — §8 (mat 4 / impl 9, weight 3): RowVersion concurrency + ConcurrencyConventionTests are enforced, but only Conference exposes mutable-update endpoints carrying tokens (Identity/Engagement have none) and SQL integration tests can't be run-verified headless (TD-03). §9 (mat 4 / impl 9): v2 is demonstrated on one endpoint only. (§24's sixteenth-cycle 9→7 recalibration is partially recovered: TD-14 shipped the per-form error summaries and the localized snackbars, lifting Implementation back to 8 in the twentieth cycle.)

    • Remediation: Run the SQL integration suite against a real SQL service in CI to exercise the 409 round-trip; extend v2 to a real evolving contract or document the deprecation policy.
    • Expected delta: Verifying concurrency lifts §8 impl 9→10; a broader v2 lifts §9 9→10.

Cross-repo comparison

How this repo's quality relates to the other consumers — where the framework's quality propagates, where a repo diverges or under-uses it, and where a consumer does better than the shared baseline — is maintained once, for all three repos, in ../Docs/Architecture/CrossRepoComparison.md (it is inherently cross-repo and would only drift if copied into each scorecard).