to navigate Enter to open "…" all these words ANDOR to combine

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 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 the workspace-internal Docs/Architecture/CrossRepoComparison.md (not published).

Rubric: ArchitectureEvaluationCriteria.md • Rubric version 2 (rebased 2026-09-04, ADR-110: §10 is Messaging & Integration Architecture at weight 3, re-scored fresh this cycle and CONFIRMED at M4/I9; §16 is AI-Native Application Architecture, scored for the first time this cycle at M2/I5 because the AI session-scoring feature calls a model in production, so its weight 2 re-enters both denominators and Σweight is 81) • Date: 2026-09-04 • 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 f831b8b8; the working tree carried one untracked helper, scripts/set-local-jwt-secrets.ps1, landing separately as ADC PR #178, and no tracked change); framework dependency pinned at MMCA.Common. v1.185.0* (all 16 packages, lockstep, Directory.Packages.props:102-125; the canonical ADR set is indexed in ../adr/README.md; framework-wide facts in ../MMCA.Common/FACTS.md). What moved this cycle (thirtieth-cycle full 34-category re-score, 2026-09-04, pin v1.185.0): three scores move, one up, one down, and one category enters the denominators. Twenty-four categories were re-confirmed first pass and ten were adversarially adjudicated. §4 Domain-Driven Design Implementation 8→9 (Maturity 4 holds): ADC PR #177 (HEAD f831b8b8) models Event.OrganizerContactEmail as the shared Email value object (Event.cs:59) and enforces its invariant inside the aggregate, Event.Create (:189) and Event.Update (:265) both route the value through Email.Create and return its failure, which closes the third ground of the twenty-seventh-cycle downgrade outright and the larger half of the second; the public-setter ground was already closed in #152. Only two optional URL fields on one aggregate keep their rules in the Application layer (EventValidationRules.cs:77,:97), trivial polish against a category otherwise met at reference quality. §9 API & Contract Design Implementation 9→8 (Maturity 4 holds; a proposed 4→3 was rejected because the discipline rides two server-required merge gates, CONTRIBUTING.md:40,:97): this is §9's first score against the rubric v2 criterion "contract tests at the boundary" (ArchitectureEvaluationCriteria.md:316), which asks for an OpenAPI baseline diff or consumer-driven contract tests plus an AsyncAPI-style async contract document. The shared OpenAPI guard is explicitly not a baseline diff (OpenApiContractTestsBase.cs:15: no committed snapshot, the assertions run against the live document), it checks well-formedness plus a paths-count floor (:64) and pinned resource strings (:78; ADC pins MinimumPathCount=10 and /Events, /Sessions, /Speakers, OpenApiContractTests.cs:17), so a renamed field or changed status code cannot fail it, and no AsyncAPI or equivalent document exists anywhere in the repo; the async contract is frozen only inside IntegrationEventContractTests.cs:9. The old row's "snapshot-guarded in CI" wording was drifted and is corrected. §16 AI-Native Application Architecture N/A→M2/I5: the rubric's applicability rule says to score the category as soon as a single product feature calls a model (ArchitectureEvaluationCriteria.md:473), and ADC has shipped organizer-facing AI session scoring against the Anthropic Messages API since 2026-04-04 (commit 5c082663; AnthropicScoringService.cs:16; key in Key Vault and injected via managed identity, infra/main.bicep:1007,:1315; supplied by deploy.yml:1122), so the 2026-09-04 N/A was wrong for this repo from the day it was written. The port is clean (IAiScoringService.cs:6 in Application, adapter and prompt in Infrastructure) and the output guardrail is strong (structured-output JSON schema with additionalProperties:false, AnthropicScoringService.cs:255; partial and refusal responses rejected, :119), but no evaluation suite of any kind exists (the only adapter tests are parse/failure contract tests against a fake handler, AnthropicScoringServiceTests.cs:39), externally submitted session and speaker text is interpolated straight into the prompt with no injection handling (:187), the prompt carries no version identifier and persisted scores record only ModelId (ScoreEventSessionsHandler.cs:83), and cost is a per-session token log line (:269) with no metric or alert. Adversarially adjudicated non-moves, all held at prior on fresh evidence: §5 (M4/I8), §7 (M4/I8), §12 (M3/I8: TD-21 is closed and Redis-backed output caching with broker-driven eviction landed, Conference.Service/Program.cs:141,:250, but the only green k6 proof, run 33589806414 at 04:10 UTC on 2026-09-02, measured a topology the same-day right-size to 0.25 vCPU replaced, infra/main.bicep:1267, and the recency-only gate never re-measures), §23 (M4/I8), §25 (M4/I7, on both axes), §27 (M4/I8, seventh rejection), §31 (M4/I8). Indices: Maturity 97.8%→96.6% (313/324) / Implementation 85.8%→85.1% (689/810); both headline percentages fall only because §16 re-enters the denominators at low scores: on the 33 categories scored last cycle, maturity held at 309 and implementation rose 678→679. The prior cycle (twenty-ninth, 2026-09-01, pin v1.179.0) moved §15 I7→8, §28 I8→9, §33 M3→4 + I8→9 and §25 I8→7; its full narrative is retained in the Indices block below.

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.179.0 (all 16, Directory.Packages.props:105-128). The standout characteristic is that architectural intent is not just documented but executable: 29 architecture-test classes (31 .cs files; 91 executed methods, re-run green 2026-07-28) 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; 90 of the 91 methods are now inherited, the §13 alert-runbook pairing gate having been lifted upstream so ObservabilityConventionTests is a bare subclass, leaving the TD-14 Profile-form guard as the single ADC-local method), 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 drilled: DISASTER-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 closed: SpeakerSelfServiceTests.cs:48-98 is now an active [Fact] (the Assert.Skip is gone) backed by a dedicated SessionIncludeChildrenRegressionTests; (3) a v2 API endpoint now exists: ServiceInfoController 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 extension point: 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 fixed: SpecificationConventionTests.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 (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' (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. Evidence: 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. Evidence: Identity.Domain/Users/User.cs:158 (Factory Create→Result<User>); Conference.Application/Events/Specifications/PublishedEventSpecification.cs:11,14 (read scoping: the BR-108 published-only criteria applied for non-organizer readers on public endpoints); 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. Evidence: 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 ↑ Implementation 8→9 (thirtieth cycle, 2026-09-04, HEAD f831b8b8): of the three grounds of the twenty-seventh-cycle downgrade, two are closed and the third is mostly closed, and the residual is trivial polish. (1) Aggregate roots carry cross-aggregate object navigations with public setters CLOSED in ADC #152 (2026-08-29) and re-verified this run: a repo-wide search for get; set; } in MMCA.ADC.Conference.Domain returns zero matches, and the object navigations that remain beside the by-ID references (Session.cs:71,:75, Activity.cs:58, Sponsor.cs:49) are private-set. (3) Event.OrganizerContactEmail is a raw string? CLOSED in ADC #177 (2026-09-03, ADR-068 drift D6): the field is now public Email? OrganizerContactEmail { get; private set; } (Event.cs:59), the private constructor takes the value object (:121), and the shared Email VO is consumed rather than re-defined (the same shape as Speaker.cs:133,:211). (2) MOSTLY CLOSED: Event.Create (:170, Email.Create at :189) and Event.Update (:247, :265) route the email through the value object and return its own invariant failure before touching the aggregate, so the email rule lives inside the boundary; the residual is that SponsorshipPacketUrl and TicketingUrl are still assigned unchecked (:289-290; the Result.Combine at :195 and :271 still validates only name/timezone/date range) while their absolute-URL and length rules live in the Application layer as EventSponsorshipPacketUrlRules / EventTicketingUrlRules (EventValidationRules.cs:77,:97, composed at :149-150), against the repo's own Sponsor.Create convention (Sponsor.cs:119). Two optional fields on one aggregate is not the "minor, isolated gaps" 7-8 band when every other criterion is met at reference quality: rich Result-returning factories with combined invariants (User.cs:167), domain events raised by the aggregates (User.cs:307, Event.cs:222,:292), encapsulated child collections behind IReadOnlyCollection (Event.cs:92,:98,:112), the newest aggregate exemplary (alias-typed ID references only, invariants, Result factory, event in the same transaction: Engagement.Domain/CheckIns/CheckIn.cs:89), and the rubric v2 tenancy criterion met by decision rather than omission (ADR-073 records ADC as deliberately single-tenant, 073-multi-tenancy-model.md:185, and no AddMultiTenancy appears in ADC source). Held below 10 by the URL residual and the retained object navigations. Maturity 4 independently re-derived: Tests/Architecture/MMCA.ADC.Architecture.Tests/Domain/EntityConventionTests.cs:3 + Domain/ImmutabilityTests.cs:3 (sealed subclasses of the shared bases; EntityConventionTestsBase.cs:18 enforces Result-returning factories, no public aggregate constructors, sealed Domain-resident entities and non-public setters) over AdcArchitectureMap.cs:28,:36,:44 (Identity, Conference, Engagement; Notification ships no Domain assembly by design, :9), gating CI via MMCA.ADC.CI.slnf:58 + deploy.yml:284
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; a proposed 8→9 was adversarially rejected in the twenty-fifth cycle, 2026-08-01, on three verified grounds): the overall solution is deliberately layered-by-project Clean Architecture, so a feature still spreads Domain/Application/Infrastructure/API across assemblies (a conscious hybrid), and a new use case still edits the per-aggregate controller plus the module's EF configuration rather than only adding a slice; the rubric's first §5 criterion also wants the DTO inside the slice, while ADC's DTOs live in the Shared assembly (Conference.Shared/Events/EventDTO.cs) with their mappers in a horizontal Application/Events/DTOs/, beside horizontal Validation/, Specifications/ and DomainEventHandlers/ folders; and enforcement covers 3 of the 4 modules, since AdcArchitectureMap.cs:12-43 omits MMCA.ADC.Notification.Application that half CLOSED 2026-09-01 (TD-19): AdcArchitectureMap.cs:51-54 now carries the Module("Notification", ...) entry, so the enforced map covers all four modules (the layered-by-project hybrid and the DTO placement remain, which is why 8 holds and why the backlog now records the residual as an unnamed lever rather than a scheduled one). Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/SliceCohesionTests.cs:8; MMCA.ADC.CI.slnf:58 (the prior :54 anchor was drifted; :54 is now Engagement.Infrastructure.Tests); deploy.yml:284 (the arch gate executes the filter; the prior :219 anchor is now the expand/contract migration guard's git fetch); ArchitectureRules.Slices.cs:13,31; AdcArchitectureMap.cs:12-54 (all four modules since 2026-09-01); 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' (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:58); 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 synchronous call graph, re-measured 2026-08-31 at seven typed gRPC client registrations across all four services with two bidirectional cycles, Conference↔Engagement (Conference.Service/Program.cs:347 vs Engagement.Service/Program.cs:276-277) and Identity↔Notification (Identity.Service/Program.cs:286-287 vs Notification.Service/Program.cs:213), over seven .proto contracts frozen by the snapshot fitness test (documented, self-healing via the resilience pipeline; AppHost deliberately omits the reciprocal WaitFor, AppHost/Program.cs:222); the enforced map still covers 3 of the 4 services (AdcArchitectureMap.cs:12-44 omits Notification, TD-19)., ADR-008/006/007; ProtoContractTests.cs:9 (seven frozen protos; the prior three-proto figure was stale); 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:27 (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 8 8/16 ↓ Implementation 9→8 (thirtieth cycle, 2026-09-04): first score against the rubric v2 criterion "contract tests at the boundary" (ArchitectureEvaluationCriteria.md:316), which is verifiably unmet on the synchronous side and only half-met on the async side; every other criterion is met well. Consistent resource shapes via the shared base controller, header-based versioning (api-version, ReportApiVersions set framework-side at MMCA.Common/.../Startup/WebApplicationBuilderExtensions.cs:253) on all 28 controllers (Conference 17, Identity 6, Engagement 5; the old "20 controllers" count was drifted), standardized RFC 9457 errors guarded by ProblemDetailsContractTestsBase.cs:74, edge validation, manual DTO decoupling (ADR), uniform pagination/sorting/filtering (X-Pagination emitted by SpeakersController.cs:194 and SessionsController.cs:201), OpenAPI served per service at /openapi/v1.json, and a real deprecated-v1.0 / v2.0 pair on ServiceInfoController.cs:18-19 proven by the CI-gated ApiVersioningTests.cs:14. What holds it at 8: the OpenAPI guard is explicitly not a baseline diff (OpenApiContractTestsBase.cs:15: there is no committed snapshot file, the assertions run against the live document; the old row's "snapshot-guarded in CI" wording was wrong), it asserts well-formedness plus a paths-count floor (:64) and the case-insensitive presence of pinned resource strings (:78), with ADC pinning MinimumPathCount=10 and /Events, /Sessions, /Speakers only (Tests/Integration/MMCA.ADC.Conference.IntegrationTests/Contract/OpenApiContractTests.cs:17), so a renamed field, a changed status code or the removal of any non-pinned route group passes; and no AsyncAPI or equivalent published async contract exists anywhere in the repo (case-insensitive search, zero files), the seven integration events being frozen byte-exactly only inside Tests/Architecture/.../Contracts/IntegrationEventContractTests.cs:9. Lever: backlog TD-24. Maturity 4 holds (a proposed 4→3 was adversarially rejected): the discipline is machine-enforced inside two server-required merge gates, integration-tests running MMCA.ADC.Integration.slnf:5 (CONTRIBUTING.md:40, branch-protection context at :97) and build-and-test running the arch tier (MMCA.ADC.CI.slnf:58, deploy.yml:284). Evidence: EventsController.cs:45 (ApiVersion), :97 ([Range])
10 Messaging & Integration Architecture 3 4 9 12/27 Rubric v2 (2026-09-04, ADR-110): category replaced in place; scores carried at prior under the merged-prior rule and re-weighted to the v2 default 3, pending the first re-score against the new criteria (broker topology by ADR, delivery semantics per consumer, dead-letter and poison handling, retention and replay, contract evolution with consumer tests, sagas, gateway/BFF discipline). Former §10 Cross-Cutting Concerns evidence, retained for the record (those facets are now scored in §5/§6/§9/§12/§17/§29): 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' (infra/main.bicep:578) + the AllowAzureServices firewall rule (:582-584) (no VNet/private endpoint, documented-accepted in infra/SQL-MANAGED-IDENTITY.md), and azureADOnlyAuthentication is not set (:593, the password path retained as a dormant fallback). Anchors re-derived 2026-08-01 (the prior :341/:345-352 were drifted). Evidence: 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); KV secrets + ACR pulled via UAMI, no admin creds; ✓ MI SQL auth ACTIVE (useManagedIdentitySql=true, password dropped, apps db_owner); ⚠ infra/main.bicep:578 publicNetworkAccess Enabled + :582-584 AllowAzureServices retained (accepted, SQL-MANAGED-IDENTITY.md)
12 Performance & Scalability 2 3 8 6/16 Uniform async (no .Result / .Wait() / GetAwaiter().GetResult() anywhere in ADC Source), 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. Real scale substance: Azure Managed Redis is the shared distributed cache, the three REST services and the Notification hub all scale to maxReplicas: 2 (the hub unpinned 2026-09-01 on the verified cross-replica proof Tests/Integration/MMCA.ADC.CrossService.IntegrationTests/CrossService/TwoReplicaHubFanOutTests.cs:49; no maxReplicas: 1 remains in infra/main.bicep), every service publishes ReadyToRun, and since the twenty-ninth cycle the Conference output cache is Redis-backed with cross-process invalidation: AddRedisOutputCaching (Source/Services/MMCA.ADC.Conference.Service/Program.cs:141) removes the cross-replica staleness ADR-040 had accepted, AddOutputCacheEvictionHandler (:250) consumes OutputCacheEvictionRequested so Application-layer and other-service writes evict tags in about a broker round trip with TTL as the backstop, over an L1/L2 HybridCache (:151) and a deny-by-default base policy (:178). 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 and asserts them as hard budgets inside the deploy-gating chromium e2e-gate. TD-21 is CLOSED (2026-09-02): the k6 capacity proof is green again through the v1.180.0 synthetic-traffic bypass (dispatch run 33589806414, 100% of checks passed, p95 139.89 ms against the 800 ms budget). Held at Implementation 8 all the same (thirtieth cycle, 2026-09-04, adversarially adjudicated; a proposed 8→9 was rejected), on one standing negative and one new one: the twenty-eighth cycle's withdrawal stands (PR #161 removed the Conference UI services' IUiReadCache opt-in after e2e-gate staleness failures and IUiReadCache still has zero occurrences in ADC Source, so the UI read tier is uncached by deliberate choice); and the only green capacity proof measured a topology that no longer exists: that run completed at 04:10 UTC on 2026-09-02, and the same day's prod cost tier 1 (ADC #173, commit 66de9341, deploy run 33665203556 at 18:06 UTC) right-sized Conference from 0.5 vCPU / 1 Gi to 0.25 vCPU / 0.5 Gi (infra/main.bicep:1267, whose own comment flags the p95 working set at 73% of the new limit and a doubled cold start) and halved the Gateway identically (:1692), with no re-measure since. Because load-freshness checks recency, not execution (deploy.yml:756, FRESHNESS_DAYS: "35" at :764, in deploy.needs at :1054), a provisioning change under a still-fresh proof triggers nothing, which is also the standing Maturity-3 basis: the k6 tier executes on a monthly cron plus dispatch only (load-test.yml:23, :14), never inside the deploy chain. The backpressure clause is only partly met as well: no PrefetchCount / ConcurrentMessageLimit / MaxConcurrentCalls configuration exists in ADC or MMCA.Common source, so broker consumers run on provider defaults. Lever for the 8→9: re-dispatch load-test.yml against the right-sized topology (effort S) and keep it green; lever for maturity 4: an in-band capacity proof. Evidence: Tests/E2E/MMCA.ADC.E2E.Tests/Workflows/WebVitalsTests.cs:17; Tests/Load/k6/conference-read-load.js:19 (PEAK_VUS default 40, CI default 67 = observed 2026 peak; p95<800ms, <1% err); .github/workflows/load-test.yml:14,:23; deploy.yml:756,:764,:1054; infra/main.bicep:1267,:1692; Source/Services/MMCA.ADC.Conference.Service/Program.cs:141,:151,:178,:250
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:508) 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:17-22) and fails the build when any sloAlertSpecs entry lacks a severity-correct runbook section or a runbook section is orphaned (ObservabilityConventionTests.cs (a sealed subclass since the 2026-07-28 extraction wave; the three [Fact]s now live in MMCA.Common.Testing.Architecture/Bases/ObservabilityConventionTestsBase.cs)), with a minimum-spec floor of 3 keeping the parse non-vacuous (base :39); the test runs in the CI.slnf arch gate (MMCA.ADC.CI.slnf:58, executed by deploy.yml:284, gating deploy via the required PR checks + deploy.yml:1054), meeting the rubric's M4 automatically-enforced bar. Implementation holds 9 (a proposed 9→10 was adversarially rejected in the twenty-fifth cycle, 2026-08-01): three ENABLED production alerts have no runbook triage section and sit outside the pairing gate's scope, including the single severity-1 alert (gateway availability, infra/main.bicep:474-498, severity: 1 at :480, enabled at :481, declared outside the sloAlertSpecs array the gate parses; re-anchored 2026-08-31), so the rubric's "runbooks for common failures" criterion is unmet. That is a real gap rather than the trivial polish the recalibrated top rung allows, and no new §13 substance has landed since the prior cycle. Evidence: MMCA.Common.Aspire/Extensions.cs:164,175 (MMCA.Common.Outbox meter + telemetry source), :330,334,350 (/health + /alive + /health/ready split; all consumed via AddServiceDefaults()); infra/main.bicep:194 (workspace App Insights), :266 (action group), :296 (sloAlertSpecs, the gate's parse surface), :326 (the SLO metricAlerts loop), :474-498 (the sev-1 gateway-availability alert, severity: 1 at :480, unpaired; anchor set re-derived 2026-08-31), :508 (SLO workbook), :529 (monthly cost budget); infra/OPERATIONS.md:1 (the prior :173/:208/:253/:226-251/:296/:317 anchors were all drifted)
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) hard-fails the deploy-gating build-and-test job (floor set at deploy.yml:319 inside the enforcement step at :296-321; re-anchored 2026-09-01 from the drifted :254/:231-256), so coverage is no longer report-only. The measured filter was narrowed on 2026-08-01 to exclude the service hosts and the protobuf-generated contracts assemblies, which are integration-tier subjects that deflated the unit-tier number (52.8% raw vs 62.5% filtered on the same run, deploy.yml:305-310). 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:1054 (deploy needs [changes, supply-chain, cost-guard, dr-freshness, load-freshness, cross-service-freshness, e2e-gate, backend-test-gate, foundation, build-images]; build-and-test/integration-tests/coverage gate as required PR checks), :254 (coverage floor 55.5%, gating); e2e.yml (workflow_call + alternating nightly legs); cross-service-tests.yml (weekday-nightly, advisory)
15 Best Practices & Code Quality 2 4 8 8/16 ↑ Implementation 7→8 (twenty-ninth cycle, 2026-09-01): two of the three grounds of the twenty-fourth-cycle downgrade are byte-verifiably CLOSED; Maturity 4 holds, independently re-derived. Enforcement is genuinely strong: five analyzers at error severity repo-wide (Directory.Build.props:70), warnings-as-errors (:10), AnalysisLevel/AnalysisMode=All (:11-12), CodeAnalysisTreatWarningsAsErrors (:13), EnforceCodeStyleInBuild (:14), blanket dotnet_analyzer_diagnostic.severity = error (.editorconfig:312), central package management with committed lock files (Directory.Build.props:36), consistent Result-pattern error handling, a single hand-written pragma disable in Source/ with an inline reason (LiveChannelPublisherGrpcAdapter.cs:43) and a Justification on all nine in-source SuppressMessage attributes (e.g. Identity.Domain/Users/User.cs:106), all enforced by the required build-and-test Release build with a --locked-mode restore (deploy.yml:264) and the gating suppress-aware vulnerable-package audit (:456-476), both required per CONTRIBUTING.md:80. Ground 1 CLOSED (suppression hygiene): Directory.Build.props (133 lines) now contains zero NuGetAuditSuppress items and no GHSA id anywhere, so the expired GHSA-2m69-gcr7-jv3q SQLite entry and the System.Private.Uri MAUI-graph entries are gone outright and ADR-038's empty accepted-advisory list is literally true; better still, the one live high advisory in the graph is remediated by a patched pin rather than suppressed (Directory.Packages.props:83, SSH.NET 2026.0.0). Ground 2 CLOSED (NoWarn hygiene): the global NoWarn line is now three codes, CS1591;EXTEXP0001;S8970 (:31), each carrying a dated justification immediately above it (:16-21, :22-26, :27-30), and RMG020 was lifted off the global line into an .Application-scoped PropertyGroup (:57, rationale :50-55), which is narrower than a justification. Ground 3 PARTIAL, and it is what holds the score at 8 rather than 9 (TD-18): the MAUI MMCA.ADC.UI project is still outside every CI build (MMCA.ADC.CI.slnf lists only UI.Web :25 and UI.Web.Client :26, and no runner installs the maui-android workload), so its analyzers/TWAE are review-only and its own NoWarn CA5392 is never gated (MMCA.ADC.UI.csproj:151), while the gating vulnerable-package scan (deploy.yml:465) is still CI.slnf-scoped. The new .github/workflows/maui-audit.yml advances that half without closing it: it audits the MAUI graph on a weekly cron (Sundays 06:00 UTC) plus dispatch (:36), but it is android-only (:19), its own header claims only the supply-chain half is closed (:14), and it has never run. Minor residual: the four-code NoWarn list CA2234;CA1054;S3459;S1144 is duplicated across five test csproj files (MMCA.ADC.CrossService.IntegrationTests.csproj:6 plus the four per-service integration test projects) despite the centralization intent stated at Directory.Build.props:40-48. Evidence: Directory.Build.props:10,:11-12,:13,:14,:16-21,:22-26,:27-30,:31,:36,:40-48,:50-55,:57,:70; .editorconfig:312; Directory.Packages.props:83 (patched SSH.NET pin), :105-128 (v1.179.0 pin); MMCA.ADC.CI.slnf:25,26; deploy.yml:264 (--locked-mode restore on the gating build), :456-476 (the audit step), :465 (the CI.slnf-scoped vulnerable-package scan); maui-audit.yml:14,19,36; CONTRIBUTING.md:80
16 AI-Native Application Architecture 2 2 5 4/10 Scored for the first time (thirtieth cycle, 2026-09-04): N/A→M2/I5. The rubric v2 N/A (2026-09-04, ADR-110) rested on "no product feature calls a language model", which was false for ADC on the day it was written: organizer-facing AI session scoring has called the Anthropic Messages API since 2026-04-04 (commit 5c082663, extended with structured outputs by ADC #176 on 2026-09-03), and it is live in production (ANTHROPIC_API_KEY supplied by deploy.yml:1122, stored in Key Vault and injected as Anthropic__ApiKey through managed identity, infra/main.bicep:1007,:1315). The rubric's applicability rule (ArchitectureEvaluationCriteria.md:473) is "score it as soon as a single feature does", so weight 2 re-enters both denominators (Σweight 79→81). Met: model calls behind a port, cleanly: IAiScoringService is declared in Application (Source/Modules/Conference/MMCA.ADC.Conference.Application/Sessions/UseCases/DecisionSupport/ScoreEventSessions/IAiScoringService.cs:6), the Anthropic HTTP adapter, its records and the prompt live in Infrastructure (Source/Modules/Conference/MMCA.ADC.Conference.Infrastructure/Sessions/Scoring/AnthropicScoringService.cs:16), registration is AddHttpClient<IAiScoringService, AnthropicScoringService> with the anthropic-version header pinned and resilience tuned in Infrastructure DI (DependencyInjection.cs:33), and the general layering fitness tests keep provider types out of Application; the output guardrail is strong (structured-output JSON schema with additionalProperties:false and a required field list, AnthropicScoringService.cs:255; partial responses rejected rather than defaulted and the refusal stop_reason handled, :119); the trigger is permission-gated and human-initiated (SessionSelectionController.cs:29), and the crash-recovery sweep never enqueues paid work nobody requested (SessionScoringSweepJob.cs:168). Tool calling and retrieval are structurally absent (no agent, no tools, no vector store), so neither criterion counts against. Open red flags (why 5, the bottom of the Adequate band): no evaluation suite of any kind, the 21 adapter tests are parse/failure contract tests against a FakeAnthropicHandler and the integration tier uses FakeAiScoringService (Tests/Modules/Conference/MMCA.ADC.Conference.Infrastructure.Tests/Services/AnthropicScoringServiceTests.cs:39), so a prompt or model change ships with no evaluation run; externally submitted session title/description and speaker tagline/bio are interpolated straight into the user prompt with no delimiting, injection handling or PII redaction (AnthropicScoringService.cs:187), and the system prompt carries no anti-injection instruction (:160); prompt and model versioning is partial, the prompt is a source-controlled const with no version identifier and the model id is a hardcoded literal (:22, claude-haiku-4-5) while persisted scores record only ModelId (ScoreEventSessionsHandler.cs:83), so a prompt edit silently changes score semantics; and observability is a per-session token log line (:269) plus a terminal-failure counter (SessionScoringProcessor.cs:96), with no token or cost instrument, no trace attributes and no runaway-spend alert. Maturity 2: the feature follows the repo's ports/adapters convention and rides the general Clean-Architecture gates, but there is no AI-specific governance: no ADR covers the feature (only ADR-061 lists the key), no evaluation gate, no prompt/model change protocol, no AI-specific fitness test, and no workflow job. Levers: backlog TD-22 (evaluation suite gating prompt/model changes) and TD-23 (injection handling, prompt version persisted with each score, cost metric). The former §16 Maintainability & Evolvability criteria moved to §34 (coupling, tech-debt register), §32 (lockstep upgrades) and §33 (onboarding); the last recorded score under that definition was M4/I8 (2026-09-01)
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 (main.bicep:578, accepted, no VNet). Evidence: deploy.yml:3-6 (push+PR), :262-281 (model-drift gate), :254 (coverage floor), blocking supply-chain audit+SBOM+licence gates (audit exit :425, SBOM exits :451/:454; re-anchored 2026-08-31), :992 (deploy needs changes+supply-chain+cost-guard+dr-freshness+load-freshness+cross-service-freshness+e2e-gate+foundation+build-images), :1099-1174 (Phase-5 smoke + auto-rollback, with an explicit alarm when a rollback itself fails), :138 (expand/contract migration guard, since revision rollback does not revert schema), OIDC no standing creds; ✓ passwordless MI DB auth active (main.bicep:574-575 SQL admin login now dormant fallback, :593 records that azureADOnlyAuthentication is deliberately unset); ⚠ main.bicep:578 public network access + :582 the AllowAzureServices firewall rule (accepted). All anchors in this row were re-derived 2026-08-01; the prior :3-8/:89-102/:67-84/:108-169/:344/:509-571/:337-338/:341 set was drifted
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:270,284), a required check on every PR (CONTRIBUTING.md:80), meeting the rubric's automatically-enforced bar (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 held it at 8 was cap pressure: code-behinds sitting within 38 lines of the enforced 400-line cap, so an added method fails the gate rather than being caught in review. Re-measured 2026-09-01 at HEAD 65bddd4b, and the picture inverted: the "flush at the cap, zero headroom" state is retired. The high-water mark is now PublicSessionDetail.razor.cs at 386 (14 lines of headroom), then SpeakerDetail 346, PublicSessionList 336, SessionLive 333, SessionFeedback 325, SessionLiveModerationPanel 316, SpeakerDashboard 314, ADCHome 313, SessionDetail 309: exactly one file is within 38 lines of the cap (was nine), and the cap itself is unchanged at 400 and unoverridden (UIArchitectureConventionTests.cs:10-12). The score holds at 8 rather than returning to 9 because the extraction pattern is applied file by file rather than systematically and the category's other criteria are unchanged, not because the ledger's cap pressure still bites. 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:284 (gates every PR); Common UIArchitectureConventionTestsBase.cs:22 (MaxCodeBehindLines => 400, MaxInlineCodeLines => 120; no ADC override, UIArchitectureConventionTests.cs:10); Conference.UI/Pages/Session/PublicSessionDetail.razor.cs (386, the current high-water mark, 14 lines of headroom) and Conference.UI/Pages/Speaker/SpeakerDetail.razor.cs (346); 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:58), 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:58; 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 31 axe test methods over roughly 29 distinct pages, re-counted 2026-08-14), 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 adc-ACCESSIBILITY-SCREENREADER-PASS.md (Website docs-src/guides/) still awaits a recorded dated pass: its results table is still the empty placeholder row `
22 Responsive & Cross-Browser 2 3 7 6/14 ↓ Implementation 8→7 (twenty-seventh cycle): the rubric's density-options criterion has zero adoption in ADC, and content reflow is only partially met on the 17 non-DataGrid table pages, including the data-dense conference-day surfaces; this names the implementation lever the backlog had carried as "not yet identified" and places the substance at the bottom of the Strong band. ↓ 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:691, in the e2e-gate job at :677, still in deploy.needs at :1054). Firefox and webkit now run only on the nightly, and since 2026-07-29 that nightly was thinned further to alternating single-engine legs: two separate crons, Monday firefox and Thursday webkit (e2e.yml:49,:50, rationale :44-48), so each non-chromium engine is verified once a week rather than both twice; on the scheduled event they are continue-on-error (e2e.yml:144). Cross-engine coverage is therefore nightly-advisory and thinner than when the maturity dropped, 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 7: the strong substance stands (fluid layouts via MudBlazor grid/breakpoints, data grids degrading to card lists on mobile with 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), but two rubric criteria are now verified short: density options have zero adoption anywhere in ADC, and content reflow is only partial on the 17 table pages that do not use the DataGrid's card-list degradation, including the conference-day surfaces. 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:699,:756,:815) so a stale or red nightly leg blocks the deploy at near-zero runner cost. Evidence: deploy.yml:677,:691,:1054; e2e.yml:44-50,144; 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:39 asserts the shared CWV budgets on four measured pages; re-anchored 2026-08-31) and the suite runs unfiltered inside the deploy-gating chromium e2e-gate (deploy.yml:677 e2e-gate job, in deploy.needs at :1054), 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, which meets the rubric's automatically-enforced bar (ArchitectureEvaluationCriteria.md:29). Implementation holds at 8. Evidence: Common EntityServiceBase.cs:51 (server-side paging/sort/filter via query params); DataGridListPageBase.cs (CTS-cancelled ServerData + prerender-bounded fetch + persist/restore; the prior line anchors drifted, re-verify at next cite); MobileInfiniteScrollList.razor:16 (@key; the prior :61,165-167 anchors exceed the 43-line file)
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 error summary with a localized heading (EventCreate.razor:162, since consolidated into the shared <ErrorSummary> component rather than an inline MudAlert; re-anchored 2026-08-31), 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:58). The formerly-raw exception snackbars stay localized whole-sentence keys (Profile.razor.cs:235 Snackbar.ChangePasswordFailed, no {ex.Message} interpolation). Held at 8 (a proposed 8→9 was adversarially rejected in the twenty-fifth cycle, 2026-08-01). The prior cycle's named 8→9 lever, a bUnit render-level assertion of the summary's error items, genuinely shipped and is CI-gated, but a fresh read finds two criteria only partially met, so the 9-10 band's "all criteria met to a high standard, reference-quality" bar is not reached: (a) client validation does not mirror the server's cross-field and format rules, which is the category's first criterion and its first red flag, and (b) the form-level error summary is present on 9 of the 21 MudForm instances CLOSED 2026-09-01: all 21 of 21 MudForm files now render an <ErrorSummary>, so the authoring surface is covered end to end. Only lever (a) remains open in the backlog's implementation band, and it is what holds the score at 8. Evidence: Tests/Architecture/MMCA.ADC.Architecture.Tests/FormsConventionTests.cs:18,32; Common FormsConventionTestsBase.cs:29; MMCA.ADC.CI.slnf:58; EventCreate.razor:8,162 (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 7 8/14 ↓ Implementation 8→7 (twenty-ninth cycle, 2026-09-01): the category's own red flag, navigation flows that drift from the documented per-role design (ArchitectureEvaluationCriteria.md:654), is live on a newly verified instance; Maturity 4 holds and the proposed 4→3 was adversarially rejected. The substance is otherwise Strong and unchanged: clean bookmarkable routes with typed/constrained parameters via RoutePaths constants, route-level [Authorize] guards (server-enforced via SSR cookie auth, not just UI hiding) on 30 page-level [Authorize(Roles = "Organizer")] pages (the prior row's "21 admin pages" was an undercount), role/claim-filtered menu rendering (Common NavMenu.razor:198-199 on RequiredRole/RequiredClaim), 404 (NotFoundPage) and 403 (dedicated <Forbidden/>, Routes.razor:26) handled in the shell, and breadcrumbs across 143 UI files. What drops it to 7 is the rubric's "flows documented and match the implementation" criterion (:647): adc-NavigationFlow.md (557 lines, Website docs-src/guides/) has zero occurrences of /activities, /engage or speaker/qr, so 7 of the 53 routable @page files are undocumented (/activities, /activities/create, /activities/{Id:int}, Pages/Activity/ActivityList.razor:2; /conference/activities; /speaker/qr, Pages/Speaker/SpeakerQr.razor:1; /engage/sponsors/{SponsorId:int}, Engagement.UI/Pages/Sponsors/SponsorVisit.razor:1; /engage/rooms/{RoomId:int}, Engagement.UI/Pages/Rooms/RoomCheckIn.razor:1), the doc's authorization enumeration at :532-534 omits all of them, and two registered nav items are undescribed (Nav.Activities, Nav.SpeakerQr, ConferenceUIModule.cs:29,39). The pages landed in PRs #116 (2026-08-13) and #127 (2026-08-19) and the doc was edited after them on 2026-08-22 (#123) without picking them up, and nothing catches that: Common's NavigationContractTests parses Common's own embedded flow doc and is not a shared base, and ADC source and tests carry zero NavigationFlow/NavigationContract references. Maturity 4 holds because the enforcement leg is intact, automatic, and demonstrably kept up with the new pages: ManagementRouteAuthorizationTests.cs:19 (sealed subclass of the shared RouteAuthorizationTestsBase, nine Organizer-only namespaces including the new Activity namespace at :35, MinimumGovernedPages 15 at :48) and IdentityRouteAuthorizationTests.cs:16, both in MMCA.ADC.CI.slnf:43,:49 and executed by the merge-gating build-and-test job (deploy.yml:284); the rubric puts criteria and red flags on the implementation axis (:37), so documentation drift scores here, not on maturity. Lever back to 8: document the seven routes and the two nav items in adc-NavigationFlow.md (effort S); an ADC-side navigation contract test modeled on Common's is the optional second lever. Evidence: Common Routes.razor:6-31, NavMenu.razor:198-199; EventCreate.razor:2; ConferenceUIModule.cs:29,39; Conference.UI.Tests/ManagementRouteAuthorizationTests.cs:19,35,48; Identity.UI.Tests/IdentityRouteAuthorizationTests.cs:16; MMCA.ADC.CI.slnf:43,49; deploy.yml:284
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). Evidence: Hosts/UI/MMCA.ADC.UI.Web/Program.cs:71,99 (AddSessionCookieAuthentication() at :71, AddCommonBlazorCsp() at :99: the CSP, token-storage, and security-headers mechanism lives in MMCA.Common and is consumed here; HttpOnly refresh cookie never JS-readable, no localStorage, locked script/connect-src), :105,126,131 (UseCommonSecurityHeaders() enforced CSP at :105, UseAntiforgery at :126, UseAuthenticatedNoStore at :131), :145 (the /client-config runtime-config fetch, no secrets in the WASM bundle); anchors re-derived 2026-08-31 from the drifted :55-58/:86-118 set
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: 68 base .resx + 68 .es.resx (counts refreshed 2026-08-31; 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:124 UseCommonRequestLocalization(), UI.Web.Client/Program.cs:86 MmcaCultureBootstrap.SetBrowserCultureAsync, User.PreferredCulture; both anchors corrected 2026-08-31 from the drifted :114/:74). 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 (the twenty-first, twenty-second, twenty-fourth, twenty-fifth and now twenty-eighth adjudications all rejected a proposed impl 8→9; the 2026-08-31 re-proposal was the fifth, on evidence that again moved against it): 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-56) of the 53 routable @page files re-counted 2026-08-31 (the prior 49 was stale, so the evidence moved further against the lift for a fifth time), and they are public by design (:31, "no login keeps the gate robust"), so the entire authenticated authoring surface is unverified for text expansion. The twenty-fifth cycle additionally surfaced a citable violation of the rubric's culture-aware-formatting criterion that the prior row's prose did not record, so the evidence has moved slightly against the lift rather than toward it. The tier is untouched since c5e6f653 (2026-07-11) and no .resx has landed since 2026-07-20, which is why the same lift keeps returning on the same evidence. As of 2026-07-28 the 8→9 lever (broadening across the authenticated surfaces) is adjudicated DEFERRED, not open: it is worth 1 weighted point of 800 and costs authenticated-login plumbing plus expansion assertions across roughly 45 pages, the weakest cost-to-benefit ratio on either band. Recorded with re-open triggers in the backlog's Deliberate/accepted section (a second locale beyond es, any RTL locale, or a reported layout regression on an authenticated page); do not re-propose it without new evidence. 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 9 12/27 ↑ Implementation 8→9 (twenty-ninth cycle, 2026-09-01): both halves of the category's own named lever (TD-20) moved on new code landed in PR #162, not on re-argued evidence; Maturity 4 holds (lifted in the fifteenth cycle when the E2E/axe layer became an enforced deploy gate). The shape was already strong: bUnit component + route-auth tests gate every PR (MMCA.ADC.CI.slnf:43,:49,:55 via deploy.yml:284, --minimum-expected-tests 1 so a discovery breakage cannot pass vacuously), a broad Playwright suite with shared page objects and abstract bases (46 page objects, 28 workflow suites, no per-app duplication) covers role journeys, 260 role/label/test-id selectors against only 5 sleep-style calls avoid timing flakiness, zero skipped or quarantined tests remain anywhere under Tests/, and axe runs in the shared Login/Register/Profile bases ADC consumes. What lifts it to 9: (1) a markup-snapshot regression tier now exists in all three bUnit projects (Conference.UI.Tests/Components/ComponentsSnapshotTests.cs:27, assertions at :68,:86,:123,:142, plus sibling files under Identity.UI.Tests and Engagement.UI.Tests: 12 [Fact]s over 12 committed .html baselines, deterministic and OS-independent, and the helper reports a missing baseline as a non-match so it cannot pass vacuously), closing the "zero visual-regression" half in the rubric's own terms and riding the required per-PR check; (2) the new backend-test-gate job (deploy.yml:394-396, rationale :376-393, "INVARIANT: a code deploy never reaches production with zero test execution") carries the exact complement of e2e-gate's condition (if non-PR, code == 'true', ui != 'true'), sits in deploy.needs (:1054) and is required at :1093, so one of the two tiers always runs on a code deploy. Held below 10: the browser layer (chromium E2E, 31 axe scans, Core Web Vitals) is still skipped on a backend/infra/script-only deploy (e2e-gate job :677 is ui-scoped at :688 and deploy accepts a skipped gate at :1092), there is no pixel visual regression, only chromium gates the deploy (:691; firefox/webkit stay advisory on the nightly, e2e.yml:144), and 12 snapshotted components is a small slice of the component surface. Prior corrections stand: E2E #5 is un-quarantined (SpeakerSelfServiceTests.cs:58, comment :49-58) with the original populator bug still guarded by the active SessionIncludeChildrenRegressionTests.cs:27, and the create-form bUnit test (EventCreateTests.cs:17) plus the SessionSpeakers regression test narrow the state-coverage gap. Evidence: ComponentsSnapshotTests.cs:27,68,86,123,142; MMCA.ADC.CI.slnf:43,49,55; deploy.yml:284,:376-396,:677,:688,:691,:1054,:1092,:1093; e2e.yml:9,144; 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, repeatedly (DISASTER-RECOVERY.md:153-158, six PITR proofs from 2026-06-20 through 2026-08-10 covering all four live databases, the newest a 2026-08-10 restore of ADC_Conference in 2.1 min vs the 2 h RTO, produced by dr-drill.yml's weekly rotation; TD-10 closed). Ledger anchors refreshed 2026-08-13 against the ADC rotation-row PR. 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 live in deploy.needs, deploy.yml:1054). The Azure Monitor DR dashboard ships (sloWorkbook in main.bicep:508, embeds workbooks/adc-slo-workbook.json)., ADR-009 (RTO/RPO + single-region acceptance); .github/workflows/deploy.yml:699 (dr-freshness job, in deploy.needs at :1054, 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 (a sealed subclass since the 2026-07-28 extraction wave; the assertion body now lives in MMCA.Common.Testing/GracefulShutdownTestsBase.cs); infra/main.bicep:668 (LTR on all 4 ADC_* DBs), :326 (the SLO metricAlerts loop), :508 (SLO workbook). SLO/workbook anchors re-derived 2026-08-31 (the prior :425/:431/:235/:278/:791 set was drifted)
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:53,61 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:363-378 (User : IAnonymizable; idempotent Anonymize in place; re-anchored 2026-08-31 from the drifted :16,322-337); 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 (a proposed 8→9 was adversarially rejected in the twenty-fifth cycle, 2026-08-01): the row's own stated 8→9 lever, automating the conference-day surge and its revert, is demonstrably not pulled. The scale-up revert is still guard-and-notify rather than auto-reverting and surge itself is a manual action, so the rubric's "reversible scale events" criterion stays unmet in its exact terms. Evidence: .github/workflows/cost-guard.yml (read-only surge-drift check, also workflow_call: fails if Container App maxReplicas>2 or any SQL DB tier!=Basic); .github/workflows/deploy.yml:665 (cost-guard job, in deploy.needs at :1054); infra/main.bicep:529 (monthly RG budget, 80%/100% alerts via the shared action group at :266); load-test.yml:13 (k6 67 VUs = observed peak); infra/main.bicep:1153,1267,1381 (the three REST apps at maxReplicas 2), :1591 (Notification pinned at 1)
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 commits 67 packages.lock.json (count re-counted 2026-08-31 via git ls-files, up from 66; RestorePackagesWithLockFile=true), and its vuln-audit + SBOM steps are 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 is 9 because the scorecard's own stated 8→9 lever is satisfied: CI restore runs --locked-mode in all four gating restore points (deploy.yml:264 build-and-test, :412 backend-test-gate, :445 supply-chain, :583 integration-tests), so lock-file drift is tamper-enforced at restore. Held below 10 only because MassTransit v8 is pinned only transitively (inherited from Common, not in ADC's own props). Evidence: Directory.Build.props:31 (RestorePackagesWithLockFile); 67 committed packages.lock.json (initially 58 at commit 6248273); deploy.yml:456-476 (audit gate, :474 exit 1, no continue-on-error), :500,:503 (SBOM gate exits), :1054 (deploy needs [...supply-chain, cost-guard, dr-freshness, load-freshness, cross-service-freshness, e2e-gate, backend-test-gate]); Directory.Packages.props:105-128 (MMCA.Common.* all pinned 1.179.0, lockstep); nuget.config:8-19 (auditSources clears→nuget.org, packageSourceMapping MMCA.*→github); maui-audit.yml (TD-18) covers the one graph the gating supply-chain job cannot see, because the MAUI head is excluded from MMCA.ADC.CI.slnf: a weekly suppress-aware dotnet list package --vulnerable --include-transitive over Source/Hosts/UI/MMCA.ADC.UI/MMCA.ADC.UI.csproj on net10.0-android (maui-audit.yml:52-53, cron :36) that exits 1 on any non-suppressed advisory (:104-145, the failure exit at :136), with a non-gating deprecated report at :147-153; scheduled and advisory, not a deploy gate, and the Apple TFMs stay uncovered on a Linux runner; ⚠ MassTransit v8 pinned only transitively via Common, not in ADC's own props. Anchors re-derived 2026-08-01 (the prior :40/:119/:273-282/:298-311/:791/:90-140 set was drifted)
33 Developer Experience & Inner Loop 2 4 9 8/18 ↑ Maturity 3→4 and Implementation 8→9 (twenty-ninth cycle, 2026-09-01): both halves of TD-17's own recorded resolution path shipped in PR #162, after five consecutive cycles of rejected lifts. The inner loop already hit 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 a full getting-started README. The blocker was the local-vs-prod broker parity red flag (ArchitectureEvaluationCriteria.md:838) plus the "advisory, rides no gate" status of the Service Bus emulator tier, and both are now false as written. Maturity 4: servicebus-emulator-smoke (cross-service-tests.yml:153) carries no continue-on-error under an "AUTHORITATIVE SINCE 2026-08-31 (TD-17)" header block (:126-137) whose :135 explicitly forbids re-adding one (the only surviving continue-on-error in the file is apphost-smoke at :204), and cross-service-freshness now selects a run in which both cross-service and servicebus-emulator-smoke concluded success (deploy.yml:874), failing the deploy otherwise (:885), with the gate in deploy.needs (:1054) and required at :1089: a red or absent Service Bus proof blocks the deploy, which is the same enforced-precondition standard that lifted §29 and §31 to maturity 4 (and it carries the same dispatch-only, justification-mandatory break-glass). Implementation 9: the parity gap is now closed at the inner loop itself rather than documented or advisory-tested: ADC_BROKER=servicebus swaps RabbitMQ for AddServiceBusEmulatorBroker (Source/Hosting/MMCA.ADC.AppHost/Program.cs:91, :93-94), running the official emulator over the same transport production runs, via the pinned MMCA.Common.Aspire.Hosting 1.179.0. Held below 10 by the divergence that remains: the default local topology is still RabbitMQ (Program.cs:86), so Service Bus is an opt-in parity profile rather than the default, and README/docs prose still describes the tier as advisory. Evidence: README.md:1,74; cross-service-tests.yml:126-137,:135,:153,:204; deploy.yml:874,:885,:1054,:1089; AppHost/Program.cs:86,:91,:93-94 and :224-282 (health-based WaitFor ordering, deliberate no-WaitFor reverse gRPC edge); Directory.Packages.props:113; CLAUDE.md (one-command dotnet run --project Source/Hosting/MMCA.ADC.AppHost → SQL/Redis/broker/MailDev + 4 services + Gateway + UI); local.props.template:5 (UseLocalMMCA)
34 Architecture Governance & Docs 2 4 9 8/18 Reference-quality governance: a maintained ADR set (001-106, canonical in ../adr/, indexed in ../adr/README.md; the range re-counted 2026-09-01) for every non-obvious decision, executable fitness functions (29 architecture-test classes (31 .cs files; 91 executed methods, last re-run green 2026-07-28) gating CI, incl. resilience/PII/concurrency/microservice-extraction/data-residency/cross-source-specification/UI-architecture/state-management/observability-pairing, 90 of the 91 methods inherited from the shared MMCA.Common.Testing.Architecture rule library after the §13 pairing gate was lifted upstream, leaving one ADC-local method (the TD-14 Profile-form guard, FormsConventionTests.cs:31), 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: ../adr/001-104 (indexed in ../adr/README.md); Tests/Architecture/MMCA.ADC.Architecture.Tests/ (31 .cs files: 29 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." The twenty-third-cycle full re-score (2026-07-23, pin v1.123.0) moved no score (both proposed maturity lifts, §12 and §21, adversarially rejected as verified non-moves). The twenty-fourth-cycle full re-score (2026-07-28, pin v1.131.0) moved one score, down: §15 Best Practices & Code Quality implementation 8→7, on suppression/NoWarn hygiene drift rather than any code-quality regression (an audit suppression expired by its own written removal condition, three unjustified global NoWarn codes, and the MAUI project sitting outside every CI build and outside the audited dependency graph). This is a third distinct shape of axis gap: not "no gate exists" (§18/§19 pre-2026-07-15) and not "the gate stopped gating" (§22), but the enforced perimeter has a documented hole in it, since maturity 4 is measured on the CI.slnf graph while one shipped project sits outside that graph entirely. §15 keeps maturity 4 and so stays in the protect set while sitting at the top of the implementation band, which is exactly the two-axis behaviour the bands exist to make visible. The same cycle adjudicated the §27 pseudo-localization lift DEFERRED after a third rejection on unchanged evidence, so it is no longer carried as an open candidacy. The twenty-fifth-cycle full re-score (2026-08-01, pin v1.135.0) moved no score, and all six adjudications were rejected implementation lifts (§5, §13, §24, §27, §31, §33). That is the cycle's actual finding and a fourth shape of axis gap: not "no gate exists", not "the gate stopped gating", not "the enforced perimeter has a hole", but one unmet criterion each, in six independent categories, where the work that would close them is named and small but has not shipped. Two of the six (§27 and §33) are now on their fourth and second consecutive rejection respectively, which is the signal that they need a decision (schedule it or record it as deliberate) rather than another re-proposal. The twenty-sixth-cycle full re-score (2026-08-14, pin v1.152.0) moved no score (all eight adjudications rejected lifts). The twenty-seventh-cycle full re-score (2026-08-23, pin v1.160.0) moved two scores, both down on the implementation axis: §4 9→8 (public-setter cross-aggregate navigations, aggregate-external validation of Event's newer optional fields, and primitive obsession on OrganizerContactEmail where the Email VO covers the same concept elsewhere) and §22 8→7 (zero density-option adoption plus partial content reflow on the non-DataGrid table pages, naming the lever the backlog had carried as "not yet identified"). Both are fresh-read recalibrations against drifted prior citations, not code regressions; §4 enters the implementation band for the first time, and §22 rises to its joint top alongside §15. The same cycle rejected all eight proposed lifts and recorded the conditionality of the e2e-gate (UI-affecting diffs only, skipped gate accepted by deploy) as TD-20 rather than a score move. The twenty-eighth-cycle full re-score (2026-08-31, pin v1.175.0) moved no score: all ten adjudications were rejected lifts (§5, §7, §12, §13, §15, §21, §23, §24, §27, §31), a fourth consecutive cycle in which every proposed lift landed short, and the cycle is otherwise an anchor-and-measurement pass (the deploy.yml and main.bicep anchor sets re-derived after wholesale drift, and TD-16 re-measured to a flush-at-cap 400 on SessionDetail.razor.cs with nine files within 38 lines of the cap). The twenty-ninth-cycle full re-score (2026-09-01, pin v1.179.0) moved four scores, three up and one down: §15 implementation 7→8, §28 implementation 8→9, §33 maturity 3→4 with implementation 8→9, and §25 implementation 8→7. §33's is the first maturity-band move since 2026-07-21 and it closes the "the gate is advisory" shape of axis gap: the Service Bus emulator parity proof dropped continue-on-error and became a deploy precondition through cross-service-freshness. §25 introduces a fifth shape: the gate is real, automatic and demonstrably kept up with the new pages, but the documentation the rubric pairs with it did not, so navigation drift scores on implementation while maturity holds at 4. The open maturity-3 set narrows to §12/§21/§22 (7 weighted points) and the implementation scores below 8 are now §22 and §25, both at 7, with §15 back in the 8 band; §12 is the one category whose closed lever (verified two-replica hub fan-out) was offset by a new open negative in the same cycle (the red k6 capacity proof, TD-21). The thirtieth cycle (2026-09-04) adds a sixth shape: a feature that predates its rubric category. §16 enters at M2/I5 because AI session scoring shipped on 2026-04-04 under no AI-specific governance and rubric v2 only named the category on 2026-09-04, so both axes are low together rather than inverted; §9 shows the v2-criterion shape already seen on Common's §7 (a criterion the code never targeted, scoring on implementation while maturity holds); and §4 closes the value-object gap that opened in the twenty-seventh cycle. The open below-maturity-4 set widens to §12/§16/§21/§22 (11 weighted points) and the implementation scores below 8 are §16 (5), §22 (7) and §25 (7).

Indices

  • Maturity index = Σ(maturity×weight) ÷ Σ(weight×4) = 313 ÷ 324 = 96.6% (down from 97.8%, 309/316: the thirtieth-cycle full re-score (2026-09-04, pin v1.185.0, HEAD f831b8b8) moved no existing maturity score but brought §16 AI-Native Application Architecture into the denominators at M2 (+4 numerator, +8 denominator: the AI session-scoring feature has called a model in production since 2026-04-04, so the rubric's applicability rule makes the category scoreable and the 2026-09-04 N/A was wrong for this repo). Proposed maturity down-moves on §9 and §25 were both adversarially rejected. The open below-maturity-4 set is §12/§16/§21/§22 (11 weighted points), and Σ(M×w) = 313 was re-summed this run rather than inherited. Prior basis follows: 309 ÷ 316 = 97.8% (rubric v2 rebase, 2026-09-04: §10 carried at M4 but re-weighted 2→3 (+4), §16 N/A removes 4×2 (−8), Σweight 80→79, no score moved; prior figure 313 ÷ 320 = 97.8%, up from 97.2%, 311/320: the twenty-ninth-cycle full re-score (2026-09-01, pin v1.179.0, HEAD 65bddd4b) lifted §33 Developer Experience & Inner Loop maturity 3→4 (+2 weighted), the first maturity-band move since 2026-07-21, after five consecutive cycles of rejected §33 lifts: the Service Bus emulator parity proof stopped being advisory (servicebus-emulator-smoke carries no continue-on-error, cross-service-tests.yml:153, under a header that forbids re-adding one, :126-137,:135) and became an enforced deploy precondition (cross-service-freshness requires both broker jobs to have concluded success, deploy.yml:874, fails the deploy otherwise :885, in deploy.needs :1054, required :1089), the same standard that lifted §29 and §31. The open maturity-3 set shrinks to §12/§21/§22 (7 weighted points), and Σ(M×w) = 313 was re-summed this run rather than inherited. Prior basis follows: 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) and again by the twenty-fourth-cycle full re-score (2026-07-28, pin v1.131.0), where the single score move was on the implementation axis (§15 8→7) and no category crossed a maturity band, so the maturity numerator, denominator and open maturity-3 set (§12/§21/§22/§33) are all unchanged. Re-confirmed again by the twenty-fifth-cycle full re-score (2026-08-01, pin v1.135.0, HEAD 995a7886): no score moved on either axis, the six adversarial adjudications were all rejected implementation lifts, and the sum was re-derived independently this run; re-confirmed once more by the twenty-sixth-cycle full re-score (2026-08-14, pin v1.152.0, HEAD 19021d93), where all eight adjudications were again rejected lifts and the numerator was re-summed unchanged (Σ(M×w) = 311, Σ(w) = 80); and again by the twenty-seventh-cycle full re-score (2026-08-23, pin v1.160.0, HEAD 96f0919a), whose two score moves were both on the implementation axis, so the maturity numerator and the open maturity-3 set (§12/§21/§22/§33) are unchanged (Σ(M×w) = 311 re-summed that run); and again by the twenty-eighth-cycle full re-score (2026-08-31, pin v1.175.0, HEAD b04b3a3e), which moved no score on either axis (the §21 maturity 3→4 proposal was adversarially rejected on the still-empty screen-reader results table and the conditional non-PR axe gate; Σ(M×w) = 311 re-summed this run, and the open maturity-3 set stays §12/§21/§22/§33).
  • Implementation index = Σ(impl×weight) ÷ Σ(weight×10) = 689 ÷ 810 = 85.1% (down from 85.8%, 678/790: the thirtieth-cycle full re-score (2026-09-04, pin v1.185.0, HEAD f831b8b8) moved three implementation scores, reconciling exactly as §4 8→9 (+3 weighted: the Email value object inside the Event aggregate, ADC #177), §9 9→8 (-2: the rubric v2 "contract tests at the boundary" criterion, unmet on the OpenAPI side and undocumented on the async side) and §16 N/A→5 (+10 numerator, +20 denominator), so on the 33 categories scored last cycle the numerator rose 678→679 while the headline fell on the wider denominator. Seven proposed lifts were adversarially rejected (§5, §7, §12, §23, §25, §27 for a seventh time, §31). The scheduling gap to 9 on every category is now 40 weighted points across 14 categories, which is what the backlog's implementation band ranks; §16 at 5, §22 at 7 and §25 at 7 are the implementation scores below 8, and Σ(I×w) = 689 was re-summed this run rather than inherited. Prior basis follows: 678 ÷ 790 = 85.8% (rubric v2 rebase, 2026-09-04: §10 carried at I9 but re-weighted 2→3 (+9), §16 N/A removes 8×2 (−16), Σweight 80→79, no score moved; prior figure 685 ÷ 800 = 85.6%, up from 85.0%, 680/800: the twenty-ninth-cycle full re-score (2026-09-01, pin v1.179.0, HEAD 65bddd4b) moved four implementation scores, three up and one down, reconciling exactly as §15 7→8 (+2 weighted: the expired audit suppression and the unjustified NoWarn codes are both gone from Directory.Build.props), §28 8→9 (+3: the markup-snapshot regression tier in all three bUnit projects plus the new backend-test-gate), §33 8→9 (+2: the ADC_BROKER=servicebus local parity profile plus the now-gating emulator proof) and §25 8→7 (-2: 7 of 53 routable pages and two nav items missing from adc-NavigationFlow.md, with no ADC-side drift gate), a net +5. The gap to a full 800 is now 35 weighted points across 14 categories, which is what the backlog's implementation band ranks; §22 at 7 and §25 at 7 are the two implementation scores below 8 (§15 rose out of that set), and Σ(I×w) = 685 was re-summed this run rather than inherited. Prior basis follows: down from 85.6%, 685/800: the twenty-seventh-cycle full re-score (2026-08-23, pin v1.160.0, HEAD 96f0919a) corrected §4 9→8 (-3 weighted: public-setter cross-aggregate navigations, aggregate-external validation of Event's newer optional fields, primitive obsession on OrganizerContactEmail) and §22 8→7 (-2 weighted: zero density-option adoption, partial content reflow on the 17 non-DataGrid table pages), both fresh-read recalibrations against drifted prior citations; the same cycle rejected all eight proposed lifts (§5, §7, §15, §17, §18, §21, §28, §31) and the sum was re-derived independently this run (Σ(I×w) = 680). The gap to a full 800 is 40 weighted points spread across 16 categories, which is what the backlog's implementation band ranks; §15 at 7 and now §22 at 7 are the two implementation scores below 8. Prior basis follows: down from 85.9%, 687/800: the twenty-fourth-cycle full re-score (2026-07-28, pin v1.131.0) corrected §15 8→7 (-2 weighted) on suppression/NoWarn hygiene drift plus the MAUI project sitting outside the CI-audited graph, the only score move of that cycle and the only rank change on either band. The §27 impl 8→9 pseudo-loc candidacy was adversarially rejected for a third consecutive cycle on byte-identical evidence and is now adjudicated DEFERRED rather than carried open. Prior basis follows: 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 was then 8 or higher). From the twenty-fourth through the twenty-sixth cycle §15 at 7 was the only implementation score below 8. Re-confirmed again by the twenty-fifth-cycle full re-score (2026-08-01, pin v1.135.0): no score moved, and the six proposed lifts (§5, §13, §24, §27, §31, §33) were each adversarially rejected against current source, so the numerator was re-derived rather than inherited (Σ(I×w) = 685 re-summed that run); the twenty-sixth-cycle full re-score (2026-08-14, pin v1.152.0, HEAD 19021d93) rejected all eight proposed lifts (§5, §7, §12 as an M3→4, §13 as a 9→10, §15, §23, §28, §31) and re-summed the same 685. The twenty-eighth-cycle full re-score (2026-08-31, pin v1.175.0, HEAD b04b3a3e) rejected all ten proposed lifts (§5, §7, §12, §13 as a 9→10, §15, §21 as an M3→4, §23, §24, §27 for a fifth time, §31 as an 8→10) and re-summed the same 680, so §15 at 7 and §22 at 7 remain the two implementation scores below 8 and the 40-point implementation gap is unchanged.
  • The implementation index reads directly against 100% (recalibrated 2026-08-01): a 10 is awardable when an implementation is almost perfect (every criterion met at reference quality, no red flags, at most trivial polish left), superseding the rubric's literal "nothing left to improve" wording. The prior "9 is the top attainable rung / 90% ceiling" line is therefore retired and removed from this block. The denominators were never scaled (they stay ×10), so these percentages remain directly comparable to every pre-recalibration cycle. Backlog scheduling still targets 9, because ranking against 10 would put nearly every strong category in the band and drown the real gaps: the 9→10 rung is recognition earned at re-score time, not scheduled work.
  • Weaker axis: Implementation (execution quality), by ~11.5 points
  • N/A (excluded from denominators): none as of the thirtieth cycle (2026-09-04). §16 AI-Native Application Architecture was N/A at the rubric v2 rebase earlier the same day on the claim that no product feature calls a model; that claim is false for ADC (AI session scoring, live since 2026-04-04), so §16 is scored (M2/I5) and its weight 2 sits in both denominators (Σweight 81). §27 Internationalization is scored (M4/I8) as of ADR-027 (which supersedes the single-locale ADR-011).
  • §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: 29 architecture-test classes / 91 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/ObservabilityConvention: 90 of the 91 methods inherited from the shared MMCA.Common.Testing.Architecture bases (ADR-015), leaving one ADC-local method (the TD-14 Profile-form guard); ADRs 001-106 (canonical in ../adr/) capture the 'why'. The counts are the 2026-07-28 measured snapshot; the later cycles did not re-run the suite.

    • 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:153-158 records six PITR restore proofs across all four live databases, newest 2026-08-10 (2.1 min vs 2 h RTO, TD-10 closed); Tests/Hosts/MMCA.ADC.Gateway.Tests/GracefulShutdownTests.cs (a sealed subclass since the 2026-07-28 extraction wave; the assertion body now lives in MMCA.Common.Testing/GracefulShutdownTestsBase.cs) CI-verifies the host drains within a bounded timeout (impl lifted 8→9 in an earlier wave); PRIVACY.md:53,61 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:71,99,105 (AddSessionCookieAuthentication()/AddCommonBlazorCsp()/UseCommonSecurityHeaders(), all 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:13 (p95<800ms, <1% errors, plus a payload-size growth tripwire); the k6 capacity proof's recency gates the deploy via the load-freshness job (deploy.yml:756-806, FRESHNESS_DAYS: "35" at :764, in deploy.needs at :1054), though the tier itself still runs monthly cron/dispatch (load-test.yml:18), which is what holds §12 at maturity 3, **and the proof is currently RED: the 2026-09-01 scheduled run failed against the gateway edge limiter, so the recency gate blocks every deploy from 2026-09-05 (TD-21, risk 4 below)**; cost-guard.yml (read-only drift check, in deploy.needs via deploy.yml:665, fails if maxReplicas>2 or any SQL DB tier!=Basic); main.bicep:529 (monthly RG budget with 80%/100% alerts); 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, a deploy-gated chromium E2E/axe suite AND a markup-snapshot regression tier: §14 (mat 4 / impl 9) and §28 (mat 4 / impl 9, lifted 2026-09-01): 1507 unit/UI Fact/Theory across 223 files; 91 architecture-test methods across 29 test classes; 303 gating integration methods across four per-service WAF tiers; and, new this cycle, 12 markup-snapshot [Fact]s over 12 committed .html baselines in all three bUnit projects (ComponentsSnapshotTests.cs:27), which fail on a missing baseline so they cannot pass vacuously. The integration tier gates every PR as a required check, and the coverage floor, cost-guard, dr-freshness, and the chromium E2E gate sit in deploy.needs (deploy.yml:1054,:319). The E2E/axe/CWV gate is still conditional (it runs only when the diff is UI-affecting and deploy accepts a skipped gate, deploy.yml:688,:1092, TD-20), but the hole is now closed from the other side: the new backend-test-gate (:394-396, in deploy.needs at :1054, required at :1093) carries the exact complementary condition, so no code deploy reaches production with zero test execution. E2E #5 is active again (un-quarantined 2026-07-19, plain [Fact] at SpeakerSelfServiceTests.cs:58) 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:691), and the nightly was thinned again on 2026-07-29 to alternating single-engine legs (e2e.yml:49,:50, engine selection at :136-138), so §22 stays at maturity 3 and needs either the two legs restored to the gate or a cross-browser-freshness job. For §28's remaining 9→10 rung: pixel visual regression, a wider snapshot slice than 12 components, and a browser tier that does not skip on backend-only deploys.
    • 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

Expected-delta note (updated 2026-08-01): several entries below record an expected lift of "impl 9→10". Under the 2026-08-01 recalibration those are attainable, not aspirational: a 10 is awarded when an implementation is almost perfect (every criterion met at reference quality, no red flags, at most trivial polish left). They still are not scheduled work: the backlog's implementation band targets 9, so a category already at 9 is at target and is not ranked there. Read a 9→10 line as "what a future re-score would need to see", not as a queued item.

  1. The AI session-scoring feature runs in production with no evaluation suite, no injection handling and no AI-specific governance: §16 (mat 2 / impl 5, weight 2, scored for the first time this cycle; the largest gap on either axis at 8 implementation-band points and 4 maturity-band points): AnthropicScoringService.cs:187 interpolates externally submitted session and speaker text straight into the prompt, the only adapter tests are parse/failure contract tests against a fake handler (AnthropicScoringServiceTests.cs:39), the prompt has no version identifier and persisted scores record only ModelId (ScoreEventSessionsHandler.cs:83), token usage is a log line (AnthropicScoringService.cs:269) with no cost metric or alert, and no ADR, fitness test or workflow job covers the feature. The port and the output schema are the strong half (IAiScoringService.cs:6, AnthropicScoringService.cs:255).

    • Remediation: TD-22: a repeatable evaluation suite (golden sessions, judge or rubric scoring, a regression threshold) that runs before a prompt or model change ships, wired as a CI job. TD-23: delimit and sanitize the untrusted text with an explicit anti-injection instruction, persist a prompt version alongside ModelId, and emit a token/cost instrument with a spend alert. Record the feature in an ADR (or an ADR-061 amendment).
    • Expected delta: TD-23 lifts §16 impl 5→7 (the two red flags close); TD-22 as a CI gate lifts maturity 2→4 and impl 7→8 or 9.
  2. 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 gated (firefox/webkit run on the alternating nightly legs, advisory) and the recorded screen-reader pass that the rubric's maturity-4 bar asks for is still the empty placeholder row in adc-ACCESSIBILITY-SCREENREADER-PASS.md:62.

    • Remediation: Record the manual screen-reader pass.
    • Expected delta: SR pass lifts §21 maturity 3→4 (weight 3), leaving §12/§16/§22 below maturity 4 (§33 left the set on 2026-09-01; §16 entered it on 2026-09-04).
  3. 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:578 publicNetworkAccess: 'Enabled' + :582-584 the AllowAzureServices 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.
  4. The enforced-analyzer perimeter still has a documented hole, but the suppression and NoWarn hygiene is CLOSED: §15 (mat 4 / impl 8, weight 2), no longer the joint-top implementation-band item (that is now §22 and §25 at implPriority 4; §15 sits at 2). Both effort-S hygiene items SHIPPED (PR #162) and are verified in current source: Directory.Build.props (133 lines) now carries zero NuGetAuditSuppress items and no GHSA id anywhere, so the expired GHSA-2m69-gcr7-jv3q SQLite entry and the System.Private.Uri MAUI-graph entries are gone and ADR-038's empty accepted-advisory list is literally true, while the one live high advisory in the graph is remediated by a patched pin (Directory.Packages.props:83, SSH.NET 2026.0.0) rather than suppressed; and the global NoWarn line is now CS1591;EXTEXP0001;S8970 (:31), each code dated and justified at :16-21, :22-26, :27-30, with RMG020 narrowed out of the global line into an .Application-scoped PropertyGroup (:57, rationale :50-55). What remains is the structural half, TD-18: the MAUI MMCA.ADC.UI project still sits outside every CI build (MMCA.ADC.CI.slnf lists only the two web UI hosts at :25,:26; no runner installs the maui-android workload), so its analyzers and TWAE are review-only and its own NoWarn CA5392 is never gated (MMCA.ADC.UI.csproj:151), while the gating vulnerable-package scan (deploy.yml:465) is still CI.slnf-scoped. The new maui-audit.yml is a partial answer rather than the fix: weekly cron plus dispatch (:36), android-only (:19), a header that claims only the supply-chain half (:14), and it has never run.

    • Remediation: Done for the hygiene half. For the structural half, bring the MAUI head inside a CI build (or at minimum get maui-audit.yml running and wire its result somewhere that can fail), verified by a full-solution package-mode restore, not CI.slnf, since the MAUI graph is exactly what CI.slnf omits. Separately, fold the four-code NoWarn list duplicated across the five test csproj files back into the centralization stated at Directory.Build.props:40-48. Store carries the identical MAUI-graph gap and should be swept in the same pass.
    • Expected delta: The two hygiene items are DONE and realized the §15 impl 7→8 lift (+2 weighted, index 85.0%→85.6%). Bringing MAUI inside a CI build is the remaining 8→9 lever, recorded as TD-18 and deliberately deferred against the 2026-07-18 Actions-minute reduction.
    • (Prior occupant, retained for the audit trail: No lock files for the consumer apps RESOLVED, TD-01 closed, §32 now mat 4 / impl 9. ADC commits 66 packages.lock.json (RestorePackagesWithLockFile=true; initially 58 at commit 6248273), its vuln-audit + SBOM are blocking PR gates, and CI restore runs --locked-mode in every gating job (deploy.yml:264, :412, :445, :583), so lock-file drift is tamper-enforced at restore. Residual: MassTransit v8 is pinned only transitively, inherited from Common, not in ADC's own props.)
  5. The backend capacity proof is green but stale, and the API contract guard cannot catch a breaking change: §12 (mat 3 / impl 8) and §9 (mat 4 / impl 8, weight 2, down from 9 this cycle): TD-21 closed on 2026-09-02 (run 33589806414, 100% of checks passed, p95 139.89 ms through the v1.180.0 synthetic-traffic bypass), but the same day's prod cost tier 1 (ADC #173) halved Conference and the Gateway to 0.25 vCPU (infra/main.bicep:1267,:1692, the comment itself noting the p95 working set at 73% of the new limit) after that run finished, and load-freshness checks recency only (deploy.yml:764), so no proof has measured the topology that is actually deployed. On §9, the shared OpenAPI guard is well-formedness plus a paths-count floor and pinned resource strings, explicitly not a baseline diff (OpenApiContractTestsBase.cs:15,:64,:78), and no AsyncAPI-style async contract document exists, which is the rubric v2 criterion §9 is now scored against.

    • Remediation: Re-dispatch load-test.yml against the right-sized topology and keep it green (effort S); commit an OpenAPI baseline per service and diff the live document against it in the integration tier, and publish the seven integration-event contracts as an AsyncAPI-style document next to it (TD-24, effort M).
    • Expected delta: A fresh proof on the deployed topology re-opens the §12 impl 8→9 case (the IUiReadCache withdrawal remains the other half); the contract diff plus async document lifts §9 impl 8→9. An in-band capacity proof would lift §12 maturity 3→4. (Prior occupants, retained for the audit trail: the §12 red-k6 / TD-21 entry closed on 2026-09-02; the §8 "concurrency thinly exercised" entry stands at mat 4 / impl 9 and is tracked only in the backlog.)

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 the workspace-internal Docs/Architecture/CrossRepoComparison.md (not published) (it is inherently cross-repo and would only drift if copied into each scorecard).