Architecture governance
MMCA.Store: Architecture Scorecard
Canonical, version-controlled scorecard for this repo: the single source of truth for MMCA.Store's architecture scores. This is Store's first in-repo governance artifact (previously its posture lived only in the workspace docs + memory). Scored against the rubric at
ArchitectureEvaluationCriteria.md; framework-wide facts in../MMCA.Common/FACTS.md. Remediation lives inRemediationBacklog.md; the cross-repo comparison in the workspace-internalDocs/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 against the v2 criteria on 2026-09-04 and CONFIRMED at M4/I8; §16 is AI-Native Application Architecture, N/A; Σweight 80→79; no score moved) • 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 at HEAD 04afa39d (clean tree); framework dependency pinned at MMCA.Common. v1.185.0* (all 17 packages, lockstep, confirmed no divergence; ADRs are canonical in Website/docs-src/adr/, count/range owned by its README.md). What moved this cycle (2026-09-04 full re-score, 34 categories, two-pass with adversarial verification, pin v1.185.0, the first cycle scored against rubric v2): no score moves. 29 categories came back CONFIRMED at their prior values and four (§15, §20, §25, §31) came back FLAG, every one an adversarial rejection of a first-pass proposal rather than a found regression: §15 held I8 (the MAUI head is still in neither solution filter, MMCA.Store.CI.slnf:24-25 lists only the two web hosts, and three global NoWarn codes at Directory.Build.props:26 still carry no rationale; the new weekly maui-audit.yml is §32 substance by its own header), §20 held I7 for a fourth consecutive cycle (zero .css files changed since 44d9304c, the razor diff is the §5 folder renames, still exactly 30 Style=/CellStyle= occurrences across 14 razor files), §25 held M4 against a proposed DOWNGRADE (all three maturity legs verified live: the three sealed *RouteAuthorizationTests in MMCA.Store.CI.slnf:40,46,52 executed by the required build-and-test check, deploy.yml:220, and again by backend-test-gate, :375; store-NavigationFlow.md documents the per-actor flows; the role-gating commit c4adff2a is in history) with Implementation 8 confirmed and a NEW implementation lever named (the claim at store-NavigationFlow.md:166 that each actor's menu shows only its diagrammed routes is falsified by the framework NavMenu.razor:142 rendering /profile/sessions for every signed-in user and by /notifications/send being routable behind a bare [Authorize]), and §31 held I8 as a VERIFIED hold (the verifier's structured return lost its implementation number to a schema retry cap, but its text confirmed Maturity 4 on a fresh read, cost-guard being a job in deploy.needs at deploy.yml:622-624/:999/:1032, and rejected the 9 on unchanged mechanism: cost-guard.yml:7 states it never mutates anything and :94 prints a manual reset, so the rubric's automated-revert criterion is still unmet, and the tag set is one uniform costCenter/component pair with no per-service dimension, infra/main.bicep:113-119; the 2026-09-02 cost pass, commit f07eb000, is real substance, 0.25 vCPU / 0.5 Gi on all five apps at :1006, 25% trace sampling :184, 300s metric export :221-222, the legacy MMCAStore database archived and dropped :620-624, a 30-day / 1 GB-per-day Log Analytics cap foundation.bicep:36-42, and a daily ACR purge :88-98, but it refines criteria already credited inside the 8). §10 is scored against the rubric v2 criteria for the first time and returned CONFIRMED M4/I8 (broker topology and transport per ADR-066, poison-message handling per ADR-087, gateway edge scope per ADR-088, saga compensation in Orders/Saga/OrderCancelledSagaHandler.cs, the frozen ProductVariantChanged wire contract in the merge-gating IntegrationEventContractTests, and the both-tier broker freshness gate at deploy.yml:770/:999), discharging its rebase-time "carried pending re-score" caveat. Citation repairs only (no substance change), wholesale this cycle because the 2026-09-02 cost pass, the 2026-09-03 backend-test-gate job and the §5 feature-by-folder renames moved almost every cited line: deploy.yml anchors above :340 re-based throughout (e2e-gate :634-649, load-freshness :714-745, deploy.needs :999, the gate block :1026-1039, the main.bicep deploy :1189-1195, smoke :1227, rollback :1235/:1333), infra/main.bicep anchors re-based throughout (sloAlertSpecs :260, workbook :527, budget :548, per-service databases :631-658, LTR policy :660-672, Service Bus :686), §31's row anchors corrected from parameters it never cited to the budget, tag and guard lines, the renamed ShoppingCarts/ cart files and Inventory/InventoryAllocationService.cs re-based, the architecture-test paths updated to their new Cqrs//Ui//Governance/ folders, Order.cs anchors re-based after the LineCount column landed, Directory.Packages.props pins :8-105, and the DR drill-history table :186-189. Indices unchanged: Maturity 97.8%, Implementation 83.9%. Prior cycle, retained (2026-09-01 full re-score, 34 categories, two-pass with adversarial verification, pin v1.179.0): no score moves. 28 categories came back CONFIRMED at their prior values and six (§7, §12, §19, §20, §21, §27) came back FLAG, every one an adversarial rejection of a proposed first-pass uplift rather than a found regression: §7 held I8 for a third consecutive cycle (the rubric's independent-deployability criterion is still only half met: the BUILD half became per-image incremental, dirty classifier deploy.yml:138-161 and matrix :848-873, but the DEPLOY half is one azure/arm-deploy of the whole main.bicep (:1115-1121) with all five container-app images pinned to the same commit sha (:1025-1029) and a whole-fleet rollback (:1147, :1191), which is the rubric's "services that must deploy together" red flag), §12 held M3 for a fifth consecutive cycle (load-test.yml and the load-freshness block are byte-identical since 063c90dd: k6 stays a monthly cron plus dispatch, load-test.yml:18/:9; the gate is push-only, deploy.yml:663, and keeps its break-glass skip, :673-690; no perf check runs on the merge path) with Implementation 8 explicitly CONFIRMED by the verifier on that same evidence, so §12 is a VERIFIED hold this cycle, not an unread carry-forward, §19 held I8 (the new M88 superseded-load guard covers only 4 of the 7 detail pages plus CatalogBrowse, leaving CategoryDetail.razor.cs:92, CustomerDetail.razor.cs:106 and InventoryItemDetail.razor.cs:87 on the identical unguarded assign-after-await shape, and the client-side IUiReadCache opt-in was withdrawn 2026-08-31, commit e60bb8be, pending a per-service invalidation story), §20 held I7 for a third consecutive cycle (zero .css files changed, still exactly 30 Style=/CellStyle= occurrences across 14 razor files, the shared semantic classes consumed by 1 of the 6 MudDataGrid list pages, and both hosts' StoreHome stylesheets still hard-coding a hex palette behind !important), §21 held M3/I8 (the manual screen-reader results log still holds only the placeholder row, store-ACCESSIBILITY-SCREENREADER-PASS.md:68, with Implementation 8 re-verified), and §27 held I8 (no .resx or localization file has changed since the 2026-08-14 pin-bump commit 9571a963; CultureInfo.InvariantCulture amount formatting and the "(s)" pluralization workaround are both still live). §5's 2026-08-23 unverified carry-forward is discharged: it returned M4/I8 CONFIRMED on a fresh read. Citation repairs only (no substance change): §7's three evidence anchors re-based, §8's two migration-gate anchors re-based (model-drift deploy.yml:263-277, expand/contract :279-325), §12's deploy-chain and k6 anchors re-based with the now-closed BulkSetInventoryHandler N+1 removed, §16's narrated pin and package count corrected, §19's DI / CartStateService / ServerTokenStorageService anchors corrected, §20's stale "zero UI files changed" basis sentence and its sibling-page count corrected, §21's and §27's e2e-gate anchors re-based, §22's cron and engine-selection anchors re-verified, and §29's drill-history anchors re-based. The header's framework package count is also corrected from 15 to 17, the count FACTS.md owns. Indices unchanged: Maturity 97.8%, Implementation 83.9%. Earlier cycle, retained (2026-08-23 full re-score, 34 categories, two-pass with adversarial verification, pin v1.160.0): no score moves. 27 categories came back CONFIRMED at their prior values and seven (§5, §6, §7, §9, §12, §20, §31) came back FLAG, every one an adversarial rejection of a proposed first-pass move rather than a found regression: §6 held 8 (the only Source-side delta on the event path since the prior pin is a comment; the cross-service flow still carries its self-documented non-atomic publish window), §7 held 8 (the new evidence is a CI fitness gate, an enforcement gain on an already-M4 category, plus a second test that is vacuous in Store; the lockstep all-services deploy cap is confirmed live), §9 held 9 against a proposed DOWNGRADE (the contract-guard evidence base grew from the narrated two files to seven across all three services plus the frozen gRPC proto contract; only the row's narration and anchors were stale), §12 held M3 (the workflow files backing the axis are byte-unchanged since the cycle that rejected the identical uplift), §20 held 7 (zero .razor/.css/.razor.cs files changed since the last scored commit; the residual count corrects 31 to 30 as a counting fix, and the StoreHome stylesheets carry an additional previously un-cited hard-coded hex palette with !important), and §31 held 8 (the reversible-scale-events criterion is still unmet in its exact terms, the identical basis on which ADC's §31 uplift was rejected on 2026-08-01; the 300s metric-export interval and tighter ACR purge refine criteria already credited inside the 8). §5 is the one special case: its first-pass scorer returned no numbers, so M4/I8 is carried forward unverified this cycle and owes a fresh read at the next re-score. Citation repairs only (no substance change): MMCA.Store.CI.slnf:53 corrected to :53 throughout (line 52 is now MMCA.Store.Identity.UI.Tests), §8's migration-gate anchors re-based (model-drift deploy.yml:216-230, expand/contract :232-274), §9's guard narration and anchors corrected, §12's controller anchor re-based, and §27's MoneyExtensions anchor extended to :54-59. Indices unchanged: Maturity 97.8%, Implementation 83.9%. Earlier cycle, retained (2026-08-14 full re-score, 34 categories, two-pass with adversarial verification, pin v1.152.0): no score moves. All 34 categories were re-scored from evidence read at HEAD 9571a963; 28 came back CONFIRMED at their prior values and six (§5, §15, §17, §19, §20, §21) came back FLAG, every one an adversarial rejection of a proposed first-pass uplift rather than a found regression: §5 held 8 (horizontal technical folders persist inside module Application layers; generic-CRUD slices dispatch to shared framework handlers), §17 held 9 (no Bicep validate/what-if before the prod run; SQL publicNetworkAccess: Enabled, the identical caveat holding ADC at 9; single prod-only environment), §19 held 8 for the second consecutive cycle (IsDrawerOpen is still publicly settable and mutated outside the notify path, CartDrawer.razor:4, CartDrawer.razor.cs:145), §20 held 7 (the ProductList remediation converted 3 attributes while 31 identical Style=/CellStyle= occurrences remain across 14 razor files, five byte-identical to the new classes), and §21 held 3/8 (the screen-reader results log still holds only the placeholder row; the one delta since the prior pin is a single added dark-palette home axe scan, now 23 scans total). §15's verify pass proposed a correction to Implementation 7 on three suppression-hygiene gaps (the expired GHSA-2m69-gcr7-jv3q audit suppression at Directory.Build.props:54 whose own removal condition is met under the v1.152.0 pin, three undocumented global NoWarn codes at :26, and the MAUI head outside all CI enforcement); the user adjudicated a hold at the prior 8 with the three gaps recorded as §15's named backlog lever. Indices unchanged: Maturity 97.8%, Implementation 83.9%. Anchor refreshes only (no substance change): §16's stale narrated pin corrected to v1.152.0, §20/§21/§22 evidence re-anchored, and §22's nightly cadence note updated (since 2026-07-29 the scheduled matrix runs one alternating engine per week, widening the per-engine blind window to 7 days). Earlier cycle, retained (2026-07-28 full re-score, 34 categories, two-pass with adversarial verification, pin v1.131.0): three scores moved. §8 Data Architecture I8→9 on substance that landed after the prior cycle: the atomic conditional-UPDATE stock decrement (SET qty = qty - n WHERE qty >= n) with deterministic variant-id lock ordering closes the oversell read-modify-write race (InventoryAllocationService.cs:71), backed by a CK_InventoryItem_AvailableQuantity_NonNegative schema CHECK constraint (InventoryItemConfiguration.cs:27), an explicit single-transaction checkout write phase with the cross-service gRPC price fetch deliberately outside the lock window (CheckOutHandler.cs:90), and a fail-closed expand/contract destructive-migration guard in the required build-and-test job (deploy.yml:190); held at 9, not 10, because Identity has no concurrency round-trip test. §22 Responsive M4→3, the reopen the 2026-07-23 drift note predicted: the deploy-gating e2e-gate passes browsers: '["chromium"]' only (deploy.yml:494) and firefox/webkit run solely on the Mon/Thu schedule where they stay continue-on-error (e2e.yml:124,131), with no cross-browser freshness job in deploy.needs, so cross-engine verification is convention-enforced (Consistent=3), not automatic; the proposed Implementation 8→7 was adversarially REJECTED as a CI-cadence change mis-posted to the substance axis, matching ADC's M3/I8 on the identical mechanism. §27 i18n I9→7→8, a corrected over-grant rather than a regression (no i18n file changed since 2026-07-17): every price renders through Money.ToDisplayString(), which hard-codes a $ glyph and formats with CultureInfo.InvariantCulture (MMCA.Common .../MoneyExtensions.cs:20,41, consumed at CatalogBrowse.razor.cs:302), the rubric's explicit "manual number formatting ignoring culture" red flag, and pluralization is the "{0} item(s)" workaround rather than the i18n mechanism (CartDrawer.resx:20); the scorer proposed 7 and the user adjudicated 8, the conservative half of the band the verifier called defensible, since the gates and coverage behind the original grant are all intact. Three further first-pass proposals were adversarially REJECTED and held at prior: §12 M3→4 and I8→9 (no new merge-path perf gate exists; load-test.yml:17-18 is still monthly cron plus dispatch, and the load-freshness gate actually GAINED a break-glass skip at deploy.yml:577-592, a weakening), §19 I8→9 (no new state-management substance since the prior pin; IsDrawerOpen is still publicly settable outside the notify path), and §30 M4/I8→M3/I7 (every cited mechanism re-read live at HEAD, no gap found). Indices Maturity 98.4%→97.8%, Implementation 83.6%→83.9%. Earlier cycles, retained below, oldest first (2026-07-03 drift-plan execution, D1/D4/D5/D8/D9/D10): §21 Accessibility M3→4 and §28 Front-End Testing M3→4 (the Playwright + axe suite now gates the deploy: e2e-gate joined deploy.yml's needs after two consecutive fully green E2E runs, 28682334766 chromium 83/83 with firefox + webkit also green, confirmed by 28683063228), §12 Performance I7→8 (client Web Vitals are now measured in CI: WebVitalsTests writes LCP/CLS/TTFB/FCP artifacts per run), §23 Front-End Performance I6→8 (the public CatalogBrowse moved to server-side paging via GetPagedAsync + bounded MobileInfiniteScrollList, and cart enrichment now uses a targeted by-variant-id batch lookup instead of fetching the whole product list), and §32 Supply-Chain I7→8 (all three CI restores run --locked-mode and the suppress-aware vulnerability audit is now gating, D8/D9). The prior cycle's moves (2026-07-02 docs sweep: §16/§25/§20 M3→4, §27 scored M4/I7, §14 I6→9, §34 I7→9) are retained in the rows below. A same-day i18n completion sweep (2026-07-03, ADR-027 Decision 9) then lifted §27 Implementation 7→8 (zero residual literals incl. the cart/checkout/Stripe snackbars, dual CI gates, MudBlazor chrome + nav localized; indices Implementation 80.3%→80.4%). A subsequent 2026-07-11 drift-convergence cycle (drift plan D1-D13, pin v1.113.0) moved six scores: §1 SOLID Implementation 8→9 (the ctor-dependency-ceiling gate ConstructorDependencyCountTests + TimeProvider injection, D9), §9 API Implementation 8→9 (the v2 ServiceInfoController + two deploy-gating Contract tests, D12), §24 Forms Maturity 3→4 (the CI-gated FormsConventionTests, D11), §28 Front-End Testing Implementation 6→8 (bUnit breadth grown to 214 facts across 40 files, D7), §29 Resilience Implementation 8→9 (the dr-freshness deploy gate + weekly dr-drill cron + GracefulShutdownTests, D3), and §21 Accessibility Maturity 4→3 with Implementation 7→8 (honest reconciliation to ADC's M3: 22 axe scans + the new screen-reader runbook, but no dated SR pass yet, D6). D2 (MI-SQL activation wiring) and D4 (cost-guard deploy gate) also landed, with no §17/§31 score move. Indices Maturity 94.4%→94.1%, Implementation 80.4%→82.5%. A 2026-07-16 full re-score (34 categories, two-pass with adversarial verification) moved three scores: §13 Observability Implementation 8→9 (both prior deductions closed: the SLO workbook is provisioned in IaC at infra/main.bicep:274 and the per-alert infra/OPERATIONS.md runbook is in-repo; Maturity holds 3 because dashboards/runbooks are IaC/review-enforced, not CI-gated), and §18 UI Architecture + §19 State Management Maturity 3→4 (the sealed UIArchitectureConventionTests and StateManagementConventionTests subclasses of the shared v1.116.0 fitness bases run non-vacuously in the deploy-gating MMCA.Store.CI.slnf on every push and PR, the same mechanism that earned ADC its M4; their proposed Implementation bumps were adversarially rejected as enforcement gains mis-posted to the substance axis). The same re-score DECLINED the recorded maturity candidacies on §12 (k6 stays monthly/on-demand, not a merge gate) and §22 (firefox/webkit are still continue-on-error in e2e.yml:71, contrary to the backlog's promotion claim), and held the §20/§24/§27 impl candidacies. §17 Implementation 8→9 additionally banked on directly verified evidence: MI-SQL is active in production (repo variable USE_MANAGED_IDENTITY_SQL=true since 2026-07-12, activation deploy 29192048197 green), correcting the row's stale inert claim. Indices Maturity 94.1%→95.9%, Implementation 82.5%→83.0%. A same-day drift-analysis fold (2026-07-16, cross-repo ADC-vs-Store comparison, each move adversarially verified) moved two more scores: §23 Maturity 3→4 (the CWV budgets are hard assertions in the deploy-gating chromium e2e-gate, the identical evidence ADC's twentieth cycle credited; the earlier same-day hold at M3 had wrongly imported §12's k6-cadence reasoning) and §32 Implementation 8→9 (capability-level parity with ADC's I9: identical --locked-mode/audit/SBOM gating; the earlier FLAG reasoned from stale scorecard text, not capability). Doc corrections in the same fold: §16's narrated pin 1.113.0→1.116.0, §32's lock-file count 49→55, and the README gained the ADC-parity broker note (§33). Indices Maturity 95.9%→96.6%, Implementation 83.0%→83.3%. A 2026-07-17 full re-score (34 categories, two-pass with adversarial verification, pin v1.117.0) moved four scores: §5 Vertical Slice M3→4 and I7→8 (the sealed SliceCohesionTests subclass runs non-vacuously in the deploy-gating MMCA.Store.CI.slnf, the identical gate ADC credits at M4/I8; the layered-by-project hybrid stays a deliberate implementation-axis cap, no longer a maturity deduction), §13 Observability M3→4 (ObservabilityConventionTests machine-enforces the sloAlertSpecs-to-OPERATIONS.md pairing in the CI merge gate, closing exactly the "not CI-gated" reasoning that held M3), §22 Responsive M3→4 (the 2026-07-16 gate flip verified live: continue-on-error in e2e.yml is scoped to scheduled non-chromium runs only, so all three engines the e2e-gate invokes can fail a deploy; note 2026-07-23: this basis drifted on 2026-07-18 when the gate was cut to chromium-only, see the §22 row), and §27 i18n I8→9 (the PseudoLocalizationTests candidacy granted: pseudo-loc sentinel, no-overflow, and en-US leak probes run over Store's own /, /catalog, and /login pages in the deploy-gating chromium e2e-gate). The narrated framework pin refreshed 1.116.0→1.117.0 throughout. Indices Maturity 96.6%→98.4%, Implementation 83.3%→83.6%.
Executive summary
MMCA.Store is a .NET 10.0 (LangVersion preview) DDD/Clean Architecture e-commerce system (Catalog, Sales, Identity modules; Stripe checkout) extracted into independently-hosted services behind a YARP gateway, collaborating via Result-over-the-wire gRPC and MassTransit integration events on the outbox pattern. It consumes the shared MMCA.Common.* framework at v1.185.0 in lockstep with MMCA.ADC. Architecturally it is at ADC parity, and one prior assumption is corrected here: Store runs database-per-service (Store_Catalog/Store_Sales/Store_Identity, each with its own dbo.OutboxMessages; the legacy single MMCAStore DB is retained read-only as an archive/rollback only), not a single shared database. Its tactical depth (Clean Architecture, DDD, CQRS, the decorator pipeline, soft-delete/audit, RowVersion concurrency) is inherited framework substance, enforced by 23 NetArchTest fitness-test classes (shared *TestsBase subclasses from MMCA.Common.Testing.Architecture plus Store-local guards such as DataResidencyTests, PiiConventionTests, and IntegrationEventContractTests, ADR-015; the compile-time layer-guard MSBuild target is MMCA.Common-internal and does not run here).
The two axes are asymmetric: Maturity 97.8% vs Implementation 83.9%. Implementation is the weaker axis by ~14 points, a wider gap than ADC's. The maturity is high because Store inherits the framework's governed mechanisms and adds a strong operational floor (two-phase Bicep IaC, OIDC + Key Vault managed identity, a post-deploy smoke gate with auto-rollback, a drilled DR restore, a cost-guard surge-drift check, a k6 load test, and now a chromium E2E/axe deploy gate at ADC parity). The former §21/§28 gate gap and the §23 catalog fetch-all are closed this cycle, and the §27 residual unlocalized strings were closed by the same-day i18n completion sweep (I7→8; zero literals, dual CI gates); the former §18/§19 review-only maturity gap closed on 2026-07-16 (both are now CI-enforced by the shared convention fitness gates at M4), and the same-day drift-fold closed §23 (the deploy-gated CWV budget assertions credited at M4, ADC parity); the 2026-07-17 re-score then closed §5 (slice cohesion CI-gated, the hybrid kept as an implementation cap), §13 (the alert-to-runbook pairing gate), and §22 (the three-engine gate flip verified live). §22 reopened to maturity 3 on 2026-07-28 when the 2026-07-18 Actions-minute reduction was scored rather than frozen: the deploy gate runs chromium only, so §12 (k6 not a merge gate), §21 (screen-reader pass pending), and §22 (cross-engine verification convention-enforced) are the three below-4 maturity categories. §14's former coverage gap is closed: the deploy-gating floor is 51.6 measured on Store's own code (+MMCA.Store.*;-*.Tests, ~54% actual). Supply-chain (§32): the vulnerability gate is NuGetAudit + TreatWarningsAsErrors at restore plus the gating suppress-aware audit, the SBOM is a hard gate, and all three CI restores run --locked-mode.
Front-end security is the standout (§26, impl 9): access token in-memory, refresh token in an HttpOnly cookie (no localStorage), a hardened origin-pinned CSP, and runtime-config fetch (no secrets in the bundle), confirmed at ADC parity. No category is N/A: §27 Internationalization is scored (M4/I8 after the 2026-07-28 correction; the 2026-07-03 completion sweep and the 2026-07-17 pseudo-loc layout-tolerance grant had carried it to I9) since the ADR-027 en-US + es localization shipped with its CI-gated translation-completeness fitness function, superseding ADR-011's single-locale exclusion; the sweep added the LocalizedTextConventionTests literal gate and removed every residual hard-coded string. The 2026-07-28 correction is not a regression: both CI gates and the pseudo-loc E2E suite are intact and un-skipped, but culture-aware number formatting and mechanism-driven pluralization, two of the rubric's five criteria, are demonstrably unmet in current code, so the I9 was an over-grant.
Scorecard
| # | Category | Weight | Maturity (0-4) | Impl (0-10) | Weighted (M·w / I·w) | Evidence / Notes |
|---|---|---|---|---|---|---|
| 1 | SOLID Principles | 3 | 4 | 9 | 12/27 | Narrow-interface ctor DI, OCP via state strategy (no type-switch); the constructor-dependency ceiling is machine-enforced by ConstructorDependencyCountTests (ceiling 7, AuthenticationService at the high-water mark, the rubric 8+-dependency red flag cleared, D7), and TimeProvider is injected over ambient DateTime.UtcNow. Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/Cqrs/ConstructorDependencyCountTests.cs:20; AuthenticationService.cs:26; OrdersController.cs:36-47; Order.cs:69-77 (FrozenDictionary<OrderStatus,IOrderState>; re-anchored 2026-09-04 after the LineCount column landed at :53) |
| 2 | Design Patterns | 2 | 4 | 9 | 8/18 | Factory→Result, payment State machine, Saga (Stripe compensation), Specification, Decorator, Outbox, Repository/UoW: idiomatic, named. Evidence: Order.cs:102,69-77 (factory at :102, state table at :69-77; re-anchored 2026-09-04); Orders/Saga/OrderCancelledSagaHandler.cs; Orders/Specifications/OrdersByCustomerSpecification.cs |
| 3 | Clean Architecture | 3 | 4 | 9 | 12/27 | NetArchTest-enforced layer rules; domain framework-pure. (The compile-time layer-guard MSBuild target is MMCA.Common-internal; it is not a Store-side gate.) Evidence: StoreArchitectureMap.cs:14-43 + LayerDependencyTests/DomainPurityTests; Order.cs:1-13 (imports only Common.Domain/Shared) |
| 4 | Domain-Driven Design | 3 | 4 | 9 | 12/27 | Aggregate root, Money VO, by-id cross-aggregate refs, domain events, invariants, factory→Result, rich state behavior; identifier aliases. Evidence: Order.cs (Money :37, OrderInvariants, OrderIdentifierType) |
| 5 | Vertical Slice Architecture | 2 | 4 | 8 | 8/16 | Cohesive command+handler+request+validator+mapper per operation; deliberate layered-by-project hybrid (cross-cutting in the pipeline). ↑ Maturity 3→4 + Implementation 7→8 (2026-07-17): slice cohesion is machine-enforced pre-merge by SliceCohesionTests (sealed subclass of the shared SliceCohesionTestsBase, two real rule facts; the base carries no minimum-scanned-types floor, and the scan is non-vacuous in practice because StoreArchitectureMap anchors real Application assemblies) in the deploy-gating MMCA.Store.CI.slnf, the identical gate ADC credits at M4/I8; the hybrid stays a deliberate implementation-axis cap (holds impl at 8, not a maturity deduction). 2026-08-23: the proposed 8→9 was adversarially rejected a second consecutive cycle: horizontal technical folders persist inside the module Application layers, gate-invisible where the validated type is cross-assembly (the co-location rule exempts them, MMCA.Common .../ArchitectureRules.Slices.cs:48; instance: Identity.Application/Users/Validation/ChangePasswordRequestValidator.cs:10 over the Shared ChangePasswordRequest while its command+handler live in Users/UseCases/ChangePassword/), and generic-CRUD operations still dispatch to shared framework handlers (Catalog.Application/DependencyInjection.cs:43, Identity.Application/DependencyInjection.cs:48) with reads served by the generic IEntityQueryService (CategoriesController.cs:34). The 2026-08-23 carry-forward debt was discharged 2026-09-01 (M4/I8 CONFIRMED on a fresh read) and both axes were re-CONFIRMED 2026-09-04. Evidence (re-anchored 2026-09-04): Tests/Architecture/MMCA.Store.Architecture.Tests/Cqrs/SliceCohesionTests.cs:9; MMCA.Store.CI.slnf:53; Application/Orders/UseCases/Cancel/{CancelOrderCommand,CancelOrderHandler}.cs |
| 6 | CQRS & Event-Driven | 2 | 4 | 8 | 8/16 | Command/query split, outbox persist-then-publish, idempotent inbox (AddInboxMessages), Stripe saga; one cross-service event flow (ProductVariantChanged) + a deliberate in-process UserRegistered domain event; the genuine outbox-to-broker-to-consumer round-trip is now covered by the non-gating nightly MMCA.Store.CrossService.IntegrationTests (Testcontainers RabbitMQ+SQL, D5). Evidence: Sales.Service/Program.cs:178-179 (re-anchored 2026-09-04); Contracts/IntegrationEventContractTests.cs; Tests/Integration/MMCA.Store.CrossService.IntegrationTests/ |
| 7 | Microservices Readiness | 3 | 4 | 8 | 12/24 | Database-per-service (corrected from the old single-DB assumption) + per-source outbox, async events + versioned gRPC, Polly resilience, extractable modules, 5 independently-built images. Minor: all per-service DBs share one physical SQL server, self-documented at infra/main.bicep:631. 2026-09-01: the proposed Implementation 8→9 was adversarially REJECTED for the third consecutive cycle. Maturity 4 re-confirmed (the §7 fitness classes, MicroserviceExtractionTests / ModuleIsolationTests / ServiceContractPurityTests / ProtoContractTests, ship in MMCA.Store.CI.slnf:53 and execute in the required build-and-test job, deploy.yml:220), but "services build/test/deploy independently" remains only half met: the BUILD half is now per-image incremental (dirty classifier deploy.yml:138-161, matrix :902-927, unchanged legs re-tagged instead of rebuilt :977-994), while the DEPLOY half is a single azure/arm-deploy of the whole main.bicep (:1189-1195) with all five container-app images pinned to one commit sha (:1091-1095), so every service takes a new revision on every deploy and a smoke failure rolls all five back together (:1235, :1333). That is the rubric's "services that must deploy together" red flag, which keeps the row in the Strong band rather than Exemplary. Evidence (re-anchored 2026-09-04, re-CONFIRMED on both axes): infra/main.bicep:631-658 (per-service DBs, names :636-640) + :686 (Service Bus namespace); Sales.Service/Program.cs:245-246 (gRPC typed clients), :256 (AddBrokerMessaging); deploy.yml:824-825 (cross-service-freshness now requires BOTH the RabbitMQ round-trip and the Service Bus emulator smoke to have succeeded in the same nightly run) |
| 8 | Data Architecture | 3 | 4 | 9 | 12/27 | Per-aggregate tx, soft-delete + filtered indexes, central audit (Common), RowVersion concurrency, versioned per-service migrations with a model-drift CI gate, LTR backups. ↑ Implementation 8→9 (2026-07-28): the write path is now race-safe by construction, not by convention. Stock decrements are an atomic conditional UPDATE (SET qty = qty - n WHERE qty >= n) with deterministic variant-id lock ordering, enlisted in the ambient transaction and stamping the audit columns explicitly because ExecuteUpdate bypasses the audit interceptor (InventoryAllocationService.cs:71); a CK_InventoryItem_AvailableQuantity_NonNegative CHECK constraint backstops it at the schema so no future path can drive stock negative (InventoryItemConfiguration.cs:27); the checkout write phase (decrements + order insert + cart transition) is one ExecuteInTransactionAsync with the cross-service gRPC price fetch deliberately outside it, so remote latency never extends lock hold time (CheckOutHandler.cs:90); and an expand/contract guard fails any PR whose new migration Up() drops a column/table/index without an EXPAND-CONTRACT-OVERRIDE marker, failing closed when the base diff is unresolvable (deploy.yml:279-325, re-anchored 2026-09-01; override marker documented at :288, fail-closed branch at :303, destructive-op scan at :317-319; policy at CONTRIBUTING.md:55). Both migration gates sit in the required build-and-test check. Held at 9, not 10: Identity carries IConcurrencyAware mutation requests but has no concurrency round-trip test, so the 409 proof covers 2 of 3 modules; note also that the shared ConcurrencyConventionTests rule scans Application types named *UpdateRequest, of which Store has none, so that particular gate is vacuous here (the substance below stands without it). Evidence: InventoryAllocationService.cs:71; InventoryItemConfiguration.cs:27; CheckOutHandler.cs:90; API-level round-trip proof incl. the child-entity token path in Tests/Integration/MMCA.Store.Catalog.IntegrationTests/Concurrency/StaleRowVersionConflictTests.cs:45 and Tests/Integration/MMCA.Store.Sales.IntegrationTests/Concurrency/OrderTransitionConcurrencyTests.cs:23; raw-IQueryable ban with an EMPTY allowlist in Tests/Architecture/MMCA.Store.Architecture.Tests/Cqrs/RawQueryableConventionTests.cs:14 (in MMCA.Store.CI.slnf:53); per-module model-drift gate deploy.yml:263-277; main.bicep:660-672 (LTR policy, re-anchored 2026-09-04) |
| 9 | API & Contract Design | 2 | 4 | 9 | 8/18 | RFC 9457 Problem Details, header versioning, pagination, DTO decoupling (ADR-001), gRPC .proto, OpenAPI served non-prod, and a demonstrated v2 contract: ServiceInfoController carries [ApiVersion("1.0", Deprecated)] + [ApiVersion("2.0")]. Narration corrected 2026-08-23 (no score impact; a proposed downgrade to 8 was adversarially REJECTED because the evidence base grew, only the row's text was stale): the guards are seven contract-guard files across all three services (OpenAPI shape/path-floor + RFC 9457 Problem Details for Catalog/Sales/Identity, incl. the Store-specific 409 stale-RowVersion probe, plus ServiceInfo API-versioning on Catalog), running in integration-tests, which is PR-only (deploy.yml:485-491, the pull_request condition at :491, absent from deploy.needs at :999; re-anchored 2026-09-04) but a server-side REQUIRED status check on main with strict=true (CONTRIBUTING.md:83), and merging to main is the prod deploy, so nothing ships without them green; ProtoContractTests additionally freezes the full cross-service gRPC wire contract in the merge-gating architecture tier (ProtoContractTests.cs:9). Below 10: OpenAPI not exposed in prod (internal behind gateway); v2 demonstrated on one anonymous diagnostic endpoint in one service (all 14 business controllers are v1.0-only). Evidence: Catalog.API/Controllers/ServiceInfoController.cs:18; Tests/Integration/MMCA.Store.Catalog.IntegrationTests/Contract/{ApiVersioningTests,OpenApiContractTests,ProblemDetailsContractTests}.cs + Sales/Identity siblings; OrdersController.cs:46,91,120,127-129 (re-anchored 2026-08-23); Sales.Service/Program.cs:158,294,311-315 (re-anchored 2026-09-04; non-prod MapOpenApi at :311-315) |
| 10 | Messaging & Integration Architecture | 3 | 4 | 8 | 12/24 | Rubric v2 (2026-09-04, ADR-110): category replaced in place and re-weighted to the v2 default 3. First re-score against the v2 criteria, same day: CONFIRMED M4/I8. Broker topology and transport are ADR-governed (ADR-066: RabbitMQ locally, Service Bus in production, Sales.Service/Program.cs:256 AddBrokerMessaging), poison-message and dead-letter handling per ADR-087 with per-service outbox/inbox retention indexes (MMCA.Store.Migrations.SqlServer.Sales/Migrations/20260725133726_AddOutboxInboxRetentionIndexes.cs) and a 30-day dead-letter retention setting (infra/main.bicep:1034), saga compensation on order cancellation (Orders/Saga/OrderCancelledSagaHandler.cs), the gateway kept to routing and auth only per ADR-088 with RouteMapTests pinning every route, the ProductVariantChanged wire contract frozen by Tests/Architecture/MMCA.Store.Architecture.Tests/Contracts/IntegrationEventContractTests.cs in the required build-and-test check, and the real broker round-trip enforced as a deploy gate: cross-service-freshness (deploy.yml:770, in deploy.needs at :999) requires both the RabbitMQ and the Service Bus emulator tiers of cross-service-tests.yml:104 to have succeeded recently. Implementation stays at the carried 8 (the scorer confirmed the criteria as met; the lever for 9 is not yet identified, see the backlog). Former §10 Cross-Cutting Concerns evidence, retained for the record (those facets are now scored in §5/§6/§9/§12/§17/§29): Pipeline decorators, typed ValidateOnStart options, CORS/versioning/rate-limit/output-cache/compression, shared Polly: none copy-pasted. Evidence: Sales.Service/Program.cs; the same IsFailure-guarded cache-eviction pattern ADR-001 describes and the ADC scorecard credits is present here too (Catalog.API/Controllers/ProductsController.cs:152,173, re-anchored 2026-09-04) |
| 11 | Security | 3 | 4 | 8 | 12/24 | RS256/JWKS (no shared secret), server-side resource authz (404-not-403), KV secrets via UAMI, EF parameterized, Stripe webhook-secret handling. Evidence (re-anchored 2026-09-04): main.bicep:786 (UAMI), :874-913 (Key Vault + secrets), :600-608 (Entra SQL admin); Sales.Service/Program.cs:174-179 (JWKS-backed bearer auth); OrdersController.cs:35,143,190,291-317 |
| 12 | Performance & Scalability | 2 | 3 | 8 | 6/16 | Async throughout, projections/AsNoTracking/paging on hot paths, tiered cache (output + Redis), stateless scale-out, a real k6 load test, and client Web Vitals measured per E2E run (LCP/CLS/TTFB/FCP written as CI artifacts, D10). 2026-09-01: the proposed Maturity 3→4 was adversarially REJECTED for the fifth consecutive cycle, and Implementation 8 was independently CONFIRMED on the same read, so this is a verified hold rather than a carry-forward. load-test.yml kept the same cadence since 063c90dd (monthly cron :21 plus workflow_dispatch :12); the only deploy-chain hook, load-freshness (deploy.yml:714), is push-only (:717), bounds staleness rather than detecting regressions, and still carries the break-glass skip that exits 0 with no capacity proof (:729-745, skip branch at :732), which the deploy reads as success (:999 needs, :1034); the Web Vitals budgets ride an e2e-gate the deploy accepts as skipped (:1038). Both axes re-CONFIRMED 2026-09-04 (the only load-test.yml delta since is the synthetic-traffic header, :6-8, :66; anchors re-based); and none of the four required merge checks is a latency gate (CONTRIBUTING.md:83). The N+1 residual this row and the backlog used to name is CLOSED: the per-item cross-service gRPC loop is now one batched GetExistingIdsAsync round trip (Sales.Application/Inventory/UseCases/BulkSet/BulkSetInventoryHandler.cs:36-41, rationale :30-34). Evidence: Tests/Load/k6/catalog-read-load.js + load-test.yml:12,21; Tests/E2E/MMCA.Store.E2E.Tests/Workflows/WebVitalsTests.cs + e2e.yml:364 (WEB_VITALS_OUTPUT_DIR); OrdersController.cs:102-112 (bounded pageSize :110, asTracking: false, field projection; re-verified 2026-09-01) |
| 13 | Observability & Operability | 2 | 4 | 9 | 8/18 | OTel logs/traces/RED metrics via ServiceDefaults (incl. MMCA.Common.Outbox), /health+/alive+/health/ready, App Insights + 3 SLO alerts + action group + saved SLO workbook provisioned in IaC, per-alert operations runbook in-repo, correlation, poll-span noise control, CI-gated graceful shutdown. ↑ Maturity 3→4 (2026-07-17): the alert-to-runbook pairing is now a CI-gated fitness function: ObservabilityConventionTests parses the embedded infra/main.bicep sloAlertSpecs and fails the merge gate on any alert without a severity-correct OPERATIONS.md section (3-spec non-vacuity floor), closing exactly the "IaC/review-enforced, not CI-gated" reasoning that held M3. Implementation 9 (2026-07-16: workbook + runbook deductions closed). Evidence (re-anchored 2026-09-04): Tests/Architecture/MMCA.Store.Architecture.Tests/Governance/ObservabilityConventionTests.cs (a sealed subclass since the 2026-07-28 extraction wave; the three [Fact]s live in MMCA.Common.Testing.Architecture/Bases/ObservabilityConventionTestsBase.cs); MMCA.Store.CI.slnf:53; main.bicep:260 (sloAlertSpecs), :527 (workbook); infra/OPERATIONS.md:16; Tests/Hosts/MMCA.Store.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) |
| 14 | Testability & Test Strategy | 3 | 4 | 9 | 12/27 | Non-inverted pyramid + arch fitness + integration tier gating deploy; the unit-tier coverage floor is 51.6 measured on Store's own code (reportgenerator +MMCA.Store.*;-*.Tests, ~54% actual), the same self-filtered gate shape as ADC. A non-gating nightly real-broker round-trip tier (MMCA.Store.CrossService.IntegrationTests, Testcontainers RabbitMQ+SQL, cross-service-tests.yml) now covers the outbox-to-broker-to-consumer flow the in-process tests only approximate (D5). Held at 9 (not 10): the broker round-trip is non-gating nightly and the SQL integration suite runs only in CI. Evidence (re-anchored 2026-09-04): deploy.yml:232-258 (coverage-floor step; floor 51.6 at :255, self-filter at :253), :220 (the MMCA.Store.CI.slnf test run); arch tests; Common.Testing.E2E |
| 15 | Best Practices & Code Quality | 2 | 4 | 8 | 8/16 | Five analyzers at error + TWAE + CPM; targeted vuln pin; consistent Result pattern; zero TODO/HACK/FIXME in Source/, every hand-written pragma carries an inline reason. 2026-08-14 verify pass: the adversarial pass proposed Implementation 7 on three suppression-hygiene gaps, user-adjudicated to hold at 8 with the gaps recorded as the backlog lever: (1) the GHSA-2m69-gcr7-jv3q audit suppression (Directory.Build.props:54) is expired by its own removal-condition comment (:45-52): Store pins v1.152.0 and Common ships the patched SQLitePCLRaw.bundle_e_sqlite3 3.0.5 directly, so the entry suppresses nothing in the audited graph; (2) three of the four global NoWarn codes (:26, CS1591;RMG020;EXTEXP0001) are undocumented, with uncommented duplicates across five test csprojs despite the centralization intent at :35-37; (3) the MAUI head is outside MMCA.Store.CI.slnf, so its analyzers/TWAE/audit are review-only. The same three-gap evidence set adjudicated ADC's §15 to Implementation 7 on 2026-07-28. 2026-09-04: a proposed 8→9 was adversarially REJECTED. Gap (1) is closed and gap (3)'s vulnerability-audit half is closed by the weekly maui-audit.yml (§32 substance by its own header, :4), but the MAUI head is still in neither solution filter (MMCA.Store.CI.slnf:24-25 lists only the two web hosts, and maui-audit.yml:16-17 says so itself) and the three NoWarn codes at Directory.Build.props:26 still carry no rationale, with five test csprojs declaring their own uncommented duplicates. Evidence (re-anchored 2026-09-04): Directory.Build.props:16-20,57-78; Directory.Packages.props:56-59,68 (the five analyzers), :110 (targeted OpenTelemetry.Api vuln pin); .editorconfig:312; deploy.yml:153 |
| 16 | AI-Native Application Architecture | 2 | N/A | N/A | — | Rubric v2 (2026-09-04, ADR-110): N/A, no product feature calls a language model (the workspace's agentic engineering tooling is scored in §33); weight 2 leaves both denominators until a feature does. The former §16 Maintainability & Evolvability criteria moved to §34 (coupling, tech-debt register), §32 (lockstep upgrades) and §33 (onboarding). Former row for the record, M4/I8: Versioned framework contracts (Common 1.185.0, all 17 packages in lockstep, matching this document's own header pin; re-verified 2026-09-04 at Directory.Packages.props:8-105) with the lockstep invariant executable: FrameworkVersionConsistencyTests asserts every MMCA.Common.* pin shares one version and fails the build on a partial sweep, running in the CI merge gate. Consumes the shared arch-test package, current CLAUDE.md, extractable modules. Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/Governance/FrameworkVersionConsistencyTests.cs; MMCA.Store.CI.slnf:53; Directory.Packages.props:8-105 |
| 17 | DevOps & Deployment | 2 | 4 | 9 | 8/18 | CI gates (build/analyzers/tests/coverage-floor/model-drift) then an integration gate then deploy with post-deploy smoke + auto-rollback; two-phase Bicep; OIDC managed identity; cost-guard/dr-drill/load-test workflows. ↑ Implementation 8→9 (2026-07-16): MI-SQL is ACTIVE in production, not inert: the full infra/SQL-MANAGED-IDENTITY.md sequence completed 2026-07-12 (repo variable USE_MANAGED_IDENTITY_SQL=true set 2026-07-12, activation deploy run 29192048197 green with the full gate chain + smoke), so all three services authenticate passwordless via managed identity; the SQL password path remains only as the documented dual-auth rollback. 2026-08-14 verify: a proposed 9→10 was adversarially REJECTED (no Bicep validate/what-if runs before the prod deploy, so an infra-only PR merges with the template unexecuted; SQL publicNetworkAccess: Enabled with the AllowAzureServices rule at infra/main.bicep:590,611-616 (re-anchored 2026-09-04), the identical caveat holding ADC at 9; single prod-only environment, ENVIRONMENT_NAME: prod hardcoded at deploy.yml:28). Evidence (re-anchored 2026-09-04): deploy.yml:1073,1170-1186 (MI-SQL wiring: the USE_MANAGED_IDENTITY_SQL env and the Stage 1 / Stage 3 parameter folds), :999 (deploy gate chain), :1227 (smoke), :1235/:1333 (rollback app list and loop); infra/SQL-MANAGED-IDENTITY.md; repo vars USE_MANAGED_IDENTITY_SQL/SQL_AAD_ADMIN_* (re-verified 2026-08-14 via gh variable list). 2026-09-03 (drift D1, no score move): the deploy path no longer admits a zero-test production rollout. A new backend-test-gate job runs the MMCA.Store.CI.slnf unit + architecture + bUnit tier under the exact complement of e2e-gate's condition (deploy.yml:347, if at :349 against e2e-gate's :645), so precisely one of the two runs on every code deploy; it is in deploy.needs (:999) and the deploy condition tolerates skipped from both (:1038-1039). Mirrors ADC's job (ADR-064). |
| 18 | UI Architecture & Components | 3 | 4 | 8 | 12/24 | Code-behind split, @inherits DataGridListPageBase<ProductDTO>, scoped cart service owns data/behavior, reuse of Common.UI primitives. ↑ Maturity 3→4 (2026-07-16): the container/presentational conventions are machine-enforced pre-merge by UIArchitectureConventionTests (sealed subclass of the shared base, 400-line code-behind cap + 120-line inline @code cap, non-vacuous MinimumCodeBehindFiles guard; largest code-behind is 368 lines), running in the deploy-gating MMCA.Store.CI.slnf on push and PR, the same mechanism as ADC's M4. Implementation holds 8: minor inline-style logic in markup remains. Evidence (re-anchored 2026-09-04): Tests/Architecture/MMCA.Store.Architecture.Tests/Ui/UIArchitectureConventionTests.cs:9; StoreArchitectureMap.cs:27,35,43; MMCA.Store.CI.slnf:53; deploy.yml:220,375; ProductList.razor.cs:15; CartDrawer.razor:49 |
| 19 | State Management & Data Flow | 3 | 4 | 8 | 12/24 | Single source of truth, scoped (no static cross-user state), unidirectional flow with OnChange+InvokeAsync(StateHasChanged)+Dispose, single-flight token hydrate. ↑ Maturity 3→4 (2026-07-16): both §19 red flags are machine-enforced pre-merge by StateManagementConventionTests (mutable-static-state reflection scan over the Layer.Ui assemblies with a non-vacuous guard, plus the singleton-*StateService source scan), in the deploy-gating MMCA.Store.CI.slnf; CartStateService is registered TryAddScoped, proving the rule the gate enforces. Implementation holds 8. 2026-09-01: the proposed 8→9 was adversarially REJECTED on two named residuals live in current code. (1) The M88 superseded-load guard added 2026-09-01 covers only OrderDetail.razor.cs:88, ProductDetail.razor.cs:80, CatalogProductDetail.razor.cs:86, ShoppingCartDetail.razor.cs:83 and CatalogBrowse.razor.cs:189; the identical unguarded assign-after-await shape is still live on CategoryDetail.razor.cs:92, CustomerDetail.razor.cs:106 and InventoryItemDetail.razor.cs:88. (2) There is no client-side staleness or invalidation strategy: the IUiReadCache opt-in was withdrawn from all six UI entity services on 2026-08-31 (commit e60bb8be) pending a per-service invalidation story, so the rubric's cached-with-invalidation criterion is carried by the server output cache alone. Evidence (re-anchored 2026-09-04, re-CONFIRMED on both axes): Tests/Architecture/MMCA.Store.Architecture.Tests/Ui/StateManagementConventionTests.cs:10; Sales.UI/DependencyInjection.cs:34 (TryAddScoped); MMCA.Store.CI.slnf:53; CartStateService.cs:63 (the OnChange publisher); CartButton.razor:33 (InvokeAsync(StateHasChanged) + Dispose); Common MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs:45-54 (the single-flight hydrate is framework code, not Store code) |
| 20 | Design System & UI Consistency | 2 | 4 | 7 | 8/14 | MudBlazor + Common.UI theme/tokens used consistently, shared grid-paging wrapper; the brand-color token convention is now CI-enforced by BrandColorTokenTests (shipped 5fbd003, guards both UI hosts' home CSS against hard-coded brand hex). 2026-08-14 verify: a proposed 7→8 was adversarially REJECTED; re-rejected 2026-08-23, again 2026-09-01 and a fourth time 2026-09-04 (2026-09-04 basis: zero .css files changed since 44d9304c and the razor diff is the §5 folder renames; 2026-09-01 basis: 45 UI files did change between 063c90dd and 44d9304c, but zero of them are .css and the residual count is unchanged at 30, so the identical evidence set stands; the one new UI fitness test since the last scored commit, SortableColumnConventionTests, is a §24 grid-sorting guard, not a design-system gate). Commit a1de5a89 converted the three previously cited ProductList.razor attributes to semantic classes (.list-search-field, .grid-cell-count, .grid-cell-actions, rules in store.css:28-40), but 30 Style=/CellStyle= occurrences remain across 14 razor files (count corrected from 31 on 2026-08-23, a counting fix, not a conversion), five byte-identical to the classes just created; the shared classes existing while 5 of the 6 MudDataGrid list pages do not use them is itself the rubric's fought-page-by-page red flag (count corrected 2026-09-01: only ProductList.razor:23,96,104 consumes them; CategoryList, OrderList, CustomerList, ShoppingCartList and InventoryItemList do not). A further previously un-cited red flag surfaced 2026-08-23: the StoreHome landing stylesheet hard-codes a hex palette alongside !important overrides with only --mmca-primary tokenized, duplicated byte-for-byte in both UI hosts (UI.Web.Client/Pages/StoreHome.razor.css:203 and the MAUI head's StoreHome.razor.css:203,271). Evidence: App.razor:11; Tests/Architecture/MMCA.Store.Architecture.Tests/Ui/BrandColorTokenTests.cs; residuals re-anchored 2026-08-14, re-verified byte-identical 2026-08-23: CategoryList.razor:23 (Style=), :79/:88 (CellStyle=), OrderList.razor:22, CustomerList.razor:81, plus CatalogBrowse.razor/CatalogProductDetail.razor/OrderLinesPanel.razor/CustomerDetail.razor |
| 21 | Accessibility (a11y) | 3 | 3 | 8 | 9/24 | Strong semantics/ARIA, stated WCAG 2.1 AA, 23 axe scans over the public, shopper, and Catalog/Sales/Identity admin surfaces plus home in both palettes (a dark-palette home scan added since the prior cycle, AccessibilityTests.cs:327-340; count refreshed 2026-08-14 from the stale 22), pinned to real WCAG 2.1 AA tags (AxeOptions.cs:17-24), and the axe suite gates the deploy (the chromium e2e-gate in deploy.yml's needs; nightly keeps the full matrix). Maturity is honestly 3, not 4 (corrected D6, re-confirmed 2026-08-14): the rubric pairs axe-in-CI with a recorded manual screen-reader pass, and the ACCESSIBILITY-SCREENREADER-PASS.md results log still holds only the placeholder row; the e2e-gate is also chromium-only, UI-change-conditioned, and a SKIPPED gate is accepted by deploy (re-anchored 2026-09-04: chromium-only at deploy.yml:648, UI-scoped at :645, skipped-tolerance at :1038). 2026-08-14 verify: proposed M3→4 and I8→9 both adversarially REJECTED; M3→4 re-REJECTED 2026-09-01 on the same unmet condition, with Implementation 8 re-verified and CONFIRMED; both axes re-CONFIRMED 2026-09-04: keyboard operability/focus order have zero automated coverage, every grid scan disables aria-input-field-name for the MudBlazor pager combobox (accepted, AxeOptions.cs:35-46), and dark-palette contrast is scanned on one page. Evidence: Tests/E2E/MMCA.Store.E2E.Tests/Workflows/AccessibilityTests.cs:25-356 (23 scans); store-ACCESSIBILITY-SCREENREADER-PASS.md:68 (Website docs-src/guides/; placeholder row only); CartDrawer.razor:9,26,33,76-99 (re-anchored 2026-09-04 after the ShoppingCarts/ folder move); deploy.yml:634-649 (e2e-gate job, rationale :636-644, UI-scoping :645, browsers: '["chromium"]' :648), :999 (e2e-gate in deploy needs), :1038 (a SKIPPED gate is accepted); re-anchored 2026-09-04 |
| 22 | Responsive & Cross-Browser | 2 | 3 | 8 | 6/16 | Fluid layouts, grid→mobile-card reflow, defined+Playwright-verified chromium/firefox/webkit matrix. ↓ Maturity 4→3 (2026-07-28), the reopen the 2026-07-23 drift note predicted: the 2026-07-18 Actions-minute reduction (commit 777348ec) cut the deploy-gating e2e-gate to chromium only (deploy.yml:494, browsers: '["chromium"]', rationale at :483-488), and firefox/webkit now run solely on the Mon/Thu schedule where they stay continue-on-error (e2e.yml:124,131), with no cross-browser freshness job in deploy.needs. Cross-engine verification is therefore convention-enforced (Consistent=3), not automatic (Optimized=4), matching ADC's M3 on the identical mechanism. The trade-off is deliberate and is recorded in the backlog's Deliberate / accepted section; the score reflects what CI enforces, which is what the rubric measures. Implementation holds 8: the proposed 8→7 was adversarially REJECTED as a CI-cadence change mis-posted to the substance axis, with no responsive-implementation regression found. Cadence update (verified 2026-08-14): since 2026-07-29 the scheduled matrix runs ONE alternating engine per week (Monday firefox, Thursday webkit), not both engines twice weekly, so the per-engine blind window is now 7 days. Evidence (re-anchored 2026-09-04): deploy.yml:634,648 (e2e-gate job, browsers: '["chromium"]'), :999 (e2e-gate in deploy needs); e2e.yml:46-47 (the two alternating crons), :133-136 (the browser matrix expression, Monday firefox at :134 and Thursday webkit at :135), :143 (schedule-scoped continue-on-error); ProductList.razor:24; CatalogBrowse.razor:86 |
| 23 | Front-End Performance | 2 | 4 | 8 | 8/16 | Server paging/debounce/@key/output-cache on admin grids; the public CatalogBrowse uses server-side paging (GetPagedAsync + MudPagination, D4) and cart enrichment resolves names via the targeted by-variant-id batch lookup. ↑ Maturity 3→4 (2026-07-16 drift-fold, adversarially verified): the Core Web Vitals budgets are HARD assertions (WebVitalsTests.cs:76-79, Assert.True per metric, no soft mode) running unfiltered inside the deploy-gating chromium e2e-gate (deploy.yml:999 needs), the identical evidence shape ADC's twentieth cycle credited M4 for; the prior M3 wrongly imported §12's k6-cadence reasoning into a category whose own enforcement is per-deploy. Evidence: Tests/E2E/MMCA.Store.E2E.Tests/Workflows/WebVitalsTests.cs:76-79; deploy.yml:634-649,999 (re-anchored 2026-09-04); CatalogBrowse.razor.cs; CartStateService.cs |
| 24 | Forms, Validation & UX Safety | 2 | 4 | 8 | 8/16 | Unsaved-changes guard with current-state accessor (9 pages), double-submit blocked, all states designed, destructive confirm, abandoned-payment recovery. ↑ Maturity 3→4 (D11): the four admin create forms' guard/dirty/validated-MudForm/Required markers are machine-enforced by the CI-gated FormsConventionTests (MinimumCreateForms=4), matching ADC on the same evidence. Client validation is MudForm-level (not full FluentValidation parity), the impl 8→9 lever. Evidence (re-anchored 2026-09-04): Tests/Architecture/MMCA.Store.Architecture.Tests/Ui/FormsConventionTests.cs; MMCA.Store.CI.slnf:53; ProductCreate.razor:8,58; CartStateService.cs:233 |
| 25 | Navigation & Information Arch | 2 | 4 | 8 | 8/16 | Bookmarkable typed routes, server-enforced [Authorize]/AuthorizeView (not UI-only), 404 handled, breadcrumbs, SSR session-cookie so deep-links/F5 don't bounce; role-guard regression is CI-gated by the three per-module *RouteAuthorizationTests (commit c4adff2), and per-actor navigation is documented in NavigationFlow.md. 2026-09-04: a proposed Maturity 4→3 was adversarially REJECTED (all three maturity legs verified live: the sealed per-module *RouteAuthorizationTests are non-vacuous, CatalogRouteAuthorizationTests.cs:27 floor 2, SalesRouteAuthorizationTests.cs:35 floor 2 plus the shared-/orders not-admin-gated guard, Identity on the shared base floor of 1, executed by the required build-and-test check at deploy.yml:220 and by backend-test-gate at :375; the identical downgrade was rejected for ADC on 2026-09-01 because doc drift belongs on the implementation axis). Implementation holds 8 with a newly named lever: store-NavigationFlow.md:166 states each actor's menu contains only the routes in its diagram, but the framework shell renders /profile/sessions for every signed-in user (MMCA.Common.UI/Layout/NavMenu.razor:142) and /notifications/send is routable behind a bare [Authorize] (MMCA.Common.UI/Pages/Notifications/NotificationSend.razor:6) with no notification services wired in the Store UI host (UI.Web/Program.cs:191 adds MMCA.Common.UI to the routable assemblies); neither route appears in any diagram. Evidence: ProductList.razor:3; CatalogRoutePaths; Tests/Modules/{Catalog,Sales,Identity}/MMCA.Store.*.UI.Tests/*RouteAuthorizationTests.cs in MMCA.Store.CI.slnf:40,46,52 (re-anchored 2026-09-04) |
| 26 | Front-End Security | 3 | 4 | 9 | 12/27 | Access token in-memory, refresh token in HttpOnly cookie (no localStorage), hardened origin-pinned CSP (no unsafe-inline script-src in prod, frame-ancestors none), runtime /client-config fetch (no secrets in bundle), UseAuthenticatedNoStore. Evidence (implementation hoisted to Common in the v1.96.0 Move-to-Common wave, consumed byte-identically here): Common MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs; Common MMCA.Common.UI/Services/Auth/WasmTokenStorageService.cs; Common MMCA.Common.UI.Web/Security/BlazorCspPolicyProvider.cs |
| 27 | Internationalization (i18n) | 1 | 4 | 8 | 4/8 | ↓ Implementation 9→8 (2026-07-28), a corrected over-grant, not a regression. No i18n file has changed since the 2026-07-17 polish wave (last touching commit 103580f7), and every gate behind the original grant is intact and un-skipped, but two of the rubric's five criteria are demonstrably unmet in current code. (1) Culture-aware formatting (half-closed as of v1.152.0, re-verified 2026-08-14): Money.ToDisplayString() no longer hard-codes a $ glyph; it resolves the symbol from the price's own currency (USD/EUR map, unknown codes render symbol-less; MMCA.Common .../MMCA.Common.UI/Extensions/MoneyExtensions.cs:18-20,54-59 (re-anchored 2026-08-23), Common change 2026-08-05, inside the v1.185.0 pin Store consumes). The remaining half stands: amounts still format with CultureInfo.InvariantCulture (:69-70, consumed at CatalogBrowse.razor.cs:302), the rubric's explicit "manual number formatting ignoring culture" red flag. This is a bypass, not missing plumbing: UseCommonRequestLocalization registers both supported cultures and supported UI cultures (MMCA.Common .../WebApplicationExtensions.cs:141). (2) Pluralization is the "(s)" workaround, not handled by the i18n mechanism (CartDrawer.resx:20 "{0} item(s)", ShoppingCartList.es.resx:11 "{0} articulo(s)"). Layout tolerance is also proven on 3 public pages only, with no RTL locale. The first-pass score was 7; 8 is the adjudicated value, the conservative half of the band the adversarial pass called defensible. The root cause is shared framework code (MoneyExtensions), so the same deduction may apply to MMCA.Common and MMCA.ADC at their next re-scores. Maturity 4 unchanged and re-CONFIRMED 2026-09-04 (both axes): both arch gates run un-skipped with live thresholds (TranslationCompletenessTests.cs:17 MinimumBaseResources=25, LocalizedTextConventionTests.cs:18 MinimumScannedFiles=40) in the required build-and-test check via MMCA.Store.CI.slnf:53 + deploy.yml:220, and the pseudo-loc E2E suite rides the deploy-gating e2e-gate (re-anchored 2026-09-04: job deploy.yml:634, chromium-only :648, in deploy needs at :999; e2e.yml:370 runs the project unfiltered). 2026-09-01: the proposed 8→9 was adversarially REJECTED and the row holds at 8. No .resx or localization file has changed since the 2026-08-14 pin-bump commit 9571a963, and both unmet criteria are still live: amounts format with CultureInfo.InvariantCulture (MoneyExtensions.cs:69-70) and pluralization is the "(s)" workaround across six resx entries in four components. Citation drift corrected again 2026-09-04: UseCommonRequestLocalization is at UI.Web/Program.cs:148 and MapCultureEndpoint at :181 (2026-09-01 cited :164/:197); AddErrorResources is at Identity.Service/Program.cs:198 (previously :189). Prior (2026-07-17), retained: the layout-tolerance lever is realized on Store's own pages: PseudoLocalizationTests activates qps-Ploc via the production /culture/set cookie mechanism and asserts the [!! sentinel, the no-horizontal-overflow expression, and a per-page en-US leak probe over /, /catalog, and /login, riding the deploy-gating chromium e2e-gate (Tests/E2E/MMCA.Store.E2E.Tests/Workflows/PseudoLocalizationTests.cs:57). Prior: Implementation 7→8 (2026-07-03 i18n completion sweep, ADR-027 Decision 9); Maturity 4 holds with a second gate. Full ADR-027 adoption (supersedes ADR-011): en-US + es .resx pairs across all three module UIs, the API error resources, both StoreHome hosts, and the three UI modules' nav items (30 base / 30 .es siblings; ~130 new key pairs on the sweep), UseCommonRequestLocalization + MapCultureEndpoint (UI.Web/Program.cs:107,140), per-module AddErrorResources (Identity.Service/Program.cs:150), User.PreferredCulture + AddUserPreferences migration. The 2026-07-03 sweep's own deductions are closed (scoped to hard-coded literals; this claim never covered the culture-formatting and pluralization criteria corrected above on 2026-07-28): zero hard-coded snackbars (35 sites to whole-sentence page keys, including the cart/checkout/Stripe strings; raw {ex.Message} never surfaces), the ErrorMessages.Success concatenation is gone (obsoleted upstream, all 28 sites swept), 33 literal breadcrumb labels localize from Breadcrumb.* keys built in OnInitialized, nav menus localize via NavItem.TitleResource + new module resx pairs, and MudBlazor built-in chrome localizes via the framework's ResxMudLocalizer (inherited). Maturity 4, now doubly gated in CI.slnf: TranslationCompletenessTests (floor raised 20→25) + the NEW LocalizedTextConventionTests (no hard-coded snackbar/title/<PageTitle>/breadcrumb/NavItem literal can ship; MinimumScannedFiles=40). Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/Ui/{TranslationCompletenessTests.cs,LocalizedTextConventionTests.cs}; Sales.UI/.../CartDrawer.razor.cs + OrderDetail.razor.cs (whole-sentence Snackbar.* keys); CatalogUIModule.cs (TitleResource + resx pair); verification: Store CI suite 1120/1120 green post-sweep |
| 28 | Front-End Testing & Quality | 3 | 4 | 8 | 12/24 | bUnit .UI.Tests gate the deploy (CI.slnf), broad state-coverage component tests (loading/empty/error/edge), page-level render/parameter/event tests, shared Common.Testing.E2E, and the Playwright suite gates the deploy (chromium e2e-gate; firefox/webkit stay advisory on the nightly matrix). ↑ Implementation 6→8 (D7): bUnit breadth grown to 214 [Fact]/[Theory] across 40 files (Catalog 63 / Sales 126 / Identity 25), the full CI gate green at 1393/1393. Evidence: Tests/Modules/{Catalog,Sales,Identity}/MMCA.Store.*.UI.Tests (214 facts / 40 files); deploy.yml (e2e-gate in deploy needs); e2e.yml (workflow_call + browsers input) |
| 29 | Resilience & Business Continuity | 3 | 4 | 9 | 12/27 | RTO/RPO per scenario, PITR(7d geo-redundant)+LTR, a drilled restore (recorded PASS 28.9 min), SLO alerts, post-deploy smoke then rollback, single-region risk explicitly accepted. Maturity 4 legitimately held (D3): a dr-freshness job is in deploy.needs and fails the deploy when the last successful dr-drill is stale, backed by a weekly dr-drill.yml cron; Implementation 8→9 on the CI-gated GracefulShutdownTests in MMCA.Store.Gateway.Tests (closing the graceful-shutdown gap vs ADC). Honest note (re-verified 2026-09-04): the DISASTER-RECOVERY.md drill-history table is stale, its last row dated 2026-07-28 (:189), while the weekly dr-drill.yml cron has kept passing through 2026-08-31, so dr-freshness is satisfied; that documentation-currency gap is what holds the row at 9 rather than 10. Evidence (re-anchored 2026-09-04): .github/workflows/deploy.yml:999 (dr-freshness in deploy.needs), :1033 (its success condition); .github/workflows/dr-drill.yml:33 (weekly cron); Tests/Hosts/MMCA.Store.Gateway.Tests/GracefulShutdownTests.cs; infra/DISASTER-RECOVERY.md:186-189 (drill-history table, the 28.9-min PASS row at :186) |
| 30 | Compliance, Privacy & Governance | 2 | 4 | 8 | 8/16 | Real erasure path (Delete+Anonymize in one UoW, ADR-005), data export, residency + PII enforced by fitness functions, auto-stamped audit fields. Evidence (re-anchored 2026-09-04): DeleteUserHandler.cs:41-67 (OnAfterSoftDeleteAsync); Customer.cs:20/User.cs:17 (IAnonymizable); Governance/DataResidencyTests.cs; PRIVACY.md:57-85 |
| 31 | Cost Efficiency / FinOps | 2 | 4 | 8 | 8/16 | Right-sized Basic per-service DBs, automated surge-drift guard, IaC budget + alerts, telemetry span filtering, resource tags. 2026-09-04: the proposed Implementation 8→9 was adversarially REJECTED (a VERIFIED hold: the verifier's structured return lost its implementation number to a schema retry cap, but its text confirmed both axes on a fresh read). Maturity 4 re-confirmed: cost-guard is a workflow_call job in deploy.needs (deploy.yml:622-624, :999) that must be exactly success (:1032), plus the Monday cron (cost-guard.yml:21). The 2026-09-02 cost pass (commit f07eb000) is genuine substance: all five container apps right-sized to 0.25 vCPU / 0.5 Gi (infra/main.bicep:1006,1162,1287,1412,1511), 25% head-based trace sampling (:184), metric export stretched to 300s (:221-222), the legacy MMCAStore database archived to a bacpac and dropped from IaC (:620-624), Log Analytics at 30-day retention with a 1 GB/day cap (foundation.bicep:36-42), and a daily ACR purge task with a buildcache-only second step (:88-98). It refines criteria already credited inside the 8; the two gaps that hold the row below 9 are unchanged in mechanism: the surge guard is explicitly read-only (cost-guard.yml:7 "never mutates anything"; :94 prints a manual reset), so the rubric's automated-or-scheduled revert for reversible scale events is unmet in its exact terms (the identical basis holding ADC's §31 at I8), and spend attribution per service is coarse (one uniform commonTags set, costCenter: 'store', for every resource at :113-119; a whole-RG budget with no tag filter at :548-576). Evidence (anchors corrected 2026-09-04; the prior :80-86,271 pointed at unrelated parameters): cost-guard.yml:7,21,94; deploy.yml:622-624,999,1032; infra/main.bicep:87-99 (alert email + monthlyBudgetAmount params), :113-119 (tags), :548-576 (budget, 80% actual / 100% forecast), :1006; infra/foundation.bicep:36-42,88-98 |
| 32 | Dependency & Supply-Chain | 2 | 4 | 9 | 8/18 | Vulnerability auditing is CI-gated twice: NuGetAudit raises advisory warnings at restore and TreatWarningsAsErrors promotes them to errors (failing build-and-test and every Docker image build), and the supply-chain job's suppress-aware audit now gates (exit 1 on any non-suppressed advisory, D8); accepted advisories are the documented NuGetAuditSuppress set. Strong provenance: 55 committed packages.lock.json + CPM + lockstep Common + transitive MassTransit-v8 pin + auditSources config, and all three CI restores run --locked-mode so committed-lock drift fails the build (D9). The SBOM is a hard gate (43d733f); the license report stays report-only by design. ↑ Implementation 8→9 (2026-07-16 drift-fold, adversarially verified): capability-level comparison found no §32 mechanism ADC's I9 has that Store lacks (identical --locked-mode restores at deploy.yml:42,123,222, identical gating audit/SBOM, byte-identical suppress/lock config); held below 10 by the same transitive-only MassTransit pin caveat as ADC. 2026-09-03 (drift D2, no score move): that parity now includes the MAUI graph. maui-audit.yml runs a weekly (Sundays 06:00 UTC, :35) suppress-aware dotnet list package --vulnerable over MMCA.Store.UI on net10.0-android and exits 1 on any non-suppressed advisory (maui-audit.yml:103-151), closing the one graph the gating supply-chain job cannot see because the MAUI head is excluded from MMCA.Store.CI.slnf. It is a scheduled audit, not a deploy gate, and the Apple TFMs stay uncovered on a Linux runner (:19-25); it mirrors ADC's maui-audit.yml (TD-18). Evidence: deploy.yml:42,123,222,133-162; Directory.Build.props:8-12,27,39; nuget.config |
| 33 | Developer Experience & Inner Loop | 2 | 4 | 8 | 8/16 | Aspire one-command local stack (SQL/Redis/RabbitMQ/MailDev/Stripe), cross-repo local.props, fast CI slnf, editorconfig parity, local→Azure parity. Evidence: CLAUDE.md:13-90; local.props.template; MMCA.Store.CI.slnf |
| 34 | Architecture Governance & Docs | 2 | 4 | 9 | 8/18 | Exemplary automated conformance (23 arch fitness-test classes over shared bases + Store-local guards) + a thorough current CLAUDE.md + ADRs governed canonically in Common (range/count owned by ../MMCA.Common/FACTS.md), completed by the in-repo two-axis scorecard + RemediationBacklog.md (the single per-repo ledger), both committed and kept current per release (the former impl-7 caveat is realized). Evidence: Tests/Architecture/* (StoreArchitectureMap, 23 test classes); CLAUDE.md; committed ArchitectureScorecard.md + RemediationBacklog.md; ADRs in MMCA.Common |
Weighted = Maturity·weight / Implementation·weight. Axis-gap finding: §21 Accessibility is honestly M3/I8 (the chromium axe gate earns Implementation 8; Maturity caps at 3 pending a recorded manual screen-reader pass, matching ADC, D6), and §22 joined it at M3/I8 on 2026-07-28 for the mirror-image reason: strong substance, an enforcement mechanism that no longer covers what it certifies. Both are the healthy direction of the gap. The widest remaining M-over-I gap is §20 (M4/I7, residual inline styles), followed by §27 (M4/I8) where the enforcement is fully automatic but two rubric criteria are unmet in code.
Indices
- Maturity index = Σ(maturity×weight) ÷ Σ(weight×4) = 309 ÷ 316 = 97.8% (2026-09-04 full re-score, the first against rubric v2: no score moved; basis 29 rescored + 4 prior, the four FLAG categories §15/§20/§25/§31 keeping their prior scores under the merged-prior rule, each a verified hold; §10 re-scored on the v2 criteria at M4 (re-weighted 2→3 at the same-day rebase, +4) and §16 N/A removes 4×2 (−8), Σweight 80→79; prior figure 313 ÷ 320 = 97.8%; §12, §21, and §22 are the three categories at maturity 3)
- Implementation index = Σ(impl×weight) ÷ Σ(weight×10) = 663 ÷ 790 = 83.9% (2026-09-04 full re-score, the first against rubric v2: no score moved; basis 29 rescored + 4 prior, with §15/§20/§25/§31 holding their prior scores under the merged-prior rule (§31's verifier text confirmed the 8 even though its structured return carried no number); §10 re-scored on the v2 criteria at I8 (re-weighted 2→3 at the same-day rebase, +8) and §16 N/A removes 8×2 (−16), Σweight 80→79; prior figure 671 ÷ 800 = 83.9%)
- The implementation index reads directly against 100% (recalibration 2026-08-01: a 10 is awardable for an almost perfect implementation, so the former "attainable ceiling" line is retired). The denominators stay ×4 and ×10 so the trend line remains comparable to every prior cycle.
- Weaker axis: Implementation (execution quality), by ~14 points.
- N/A (excluded from denominators): §16 AI-Native Application Architecture (rubric v2, 2026-09-04: no product feature calls a model; weight 2 leaves both denominators, Σweight 79). §27 joined the denominators on the 2026-07-02 cycle (ADR-027 superseded ADR-011) and remains scored. §32 weight = 2 (default; raised to 3 only for the published framework MMCA.Common).
Top 5 strengths
- Clean Architecture + DDD + CQRS depth, fitness-enforced: §3/§4 (impl 9): inherited framework substance, guarded by the shared NetArchTest suite (23 test classes,
StoreArchitectureMap.cs:14-43). - Full microservices parity on a race-safe data layer, §7 (impl 8) / §8 (impl 9): database-per-service + per-service outbox + versioned gRPC + MassTransit broker + RS256/JWKS, extractable modules (re-anchored 2026-09-04:
infra/main.bicep:631-658per-service DBs,:686Service Bus), with the oversell race closed by an atomic conditional UPDATE under deterministic lock ordering plus a schema CHECK backstop (InventoryAllocationService.cs:71,InventoryItemConfiguration.cs:27) and destructive migrations blocked at the merge gate (deploy.yml:279-325). (Corrects the prior "single shared DB" assumption.) - Exemplary DevOps / operational floor: §17 (impl 9) / §31 / §12: two-phase Bicep, OIDC + Key Vault MI, passwordless MI-SQL active in prod (2026-07-12), post-deploy smoke gate with auto-rollback (re-anchored 2026-09-04:
deploy.yml:1227, rollback app list at:1235),cost-guard/dr-drill/load-testworkflows. - Reference front-end security, §26 (impl 9): in-memory access token + HttpOnly refresh cookie (no localStorage), hardened origin-pinned CSP, runtime-config fetch (
ServerTokenStorageService.cs:10-13,BlazorCspPolicyProvider.cs:71). - Drilled DR + real erasure path (§29 impl 9 / §30 impl 8): recorded 28.9-min restore (
DISASTER-RECOVERY.md:186, drill-history table:186-189), thedr-freshnessdeploy gate + weekly cron + CI-gatedGracefulShutdownTests,IAnonymizableanonymize-in-place + export + residency/PII fitness functions.
Top 5 risks
- Accessibility maturity is capped pending a human pass: §21 (mat 3, weight 3): the 23-scan axe suite gates the deploy (impl 8), but the rubric pairs axe-in-CI with a recorded manual screen-reader pass and the
ACCESSIBILITY-SCREENREADER-PASS.mdresults log is still empty (matching ADC's M3; re-verified 2026-09-04 atstore-ACCESSIBILITY-SCREENREADER-PASS.md:68, placeholder row only).- Remediation: record a dated NVDA/VoiceOver pass in the runbook against the running app (needs a human; cannot be done headless). Expected: §21 mat 3→4.
- Load evidence is capacity-planning cadence, not a merge gate: §12 (mat 3): the k6 suite is real with pass/fail thresholds and a
load-freshnessdeploy gate, but the test itself runs monthly/on-demand (load-test.yml:21cron,workflow_dispatchat:12; re-verified 2026-09-04, the only delta since being the synthetic-traffic header), so a latency regression can merge and deploy inside the freshness window. The window loosened further on 2026-07-28:load-freshnessgained a break-glass skip (re-anchored 2026-09-04:deploy.yml:729-745, skip branch at:732, job at:714), justified but a weakening of the only deploy-chain hook §12 has.- Remediation: right-size deliberately: either accept the monthly cadence as the recorded posture (matching ADC's accepted §12 M3) or add a cheap latency-regression smoke to the merge path. Expected: decision recorded either way; §12 mat 3→4 only with a gate.
- Design-system residual inline styles: §20 (impl 7, the widest M-over-I gap): the token convention is CI-enforced (
BrandColorTokenTests), and the semantic-class pattern now exists (commita1de5a89converted ProductList's three attributes to.list-search-field/.grid-cell-count/.grid-cell-actions,store.css:28-40), but 30Style=/CellStyle=occurrences remain across 14 razor files (count corrected from 31 on 2026-08-23), five byte-identical to those classes (CategoryList.razor:23,79,88;OrderList.razor:22;CustomerList.razor:81; re-verified byte-identical 2026-09-01 and 2026-09-04).- Remediation: sweep the remaining occurrences onto the now-existing semantic classes (the sibling admin list pages first, where the classes are byte-identical drop-ins). Expected: §20 impl 7→8.
- Client validation short of server parity: §24 (impl 8): the four create forms' guard/dirty/validated-
MudFormmarkers are CI-gated (FormsConventionTests, M4), but client validation stays MudForm-level rather than full FluentValidation parity with the server rules.- Remediation: mirror the remaining server-only rules client-side where they do not need the DB. Expected: §24 impl 8→9.
- Cross-engine verification no longer gates a deploy: §22 (mat 3, reopened 2026-07-28, posture widened since): the
e2e-gateruns chromium only (re-anchored 2026-09-04:deploy.yml:648, job at:634), and since 2026-07-29 the scheduled matrix runs one alternating engine per week (Monday firefox, Thursday webkit; cronse2e.yml:46-47, matrix expression:133-136) where it stayscontinue-on-error(:143), so a webkit-only layout or API break can reach production and sit undetected for up to 7 days per engine. The Actions-minute saving is real and the trade-off is recorded, so this is a priced risk, not an oversight.- Remediation: either add a
cross-browser-freshnessjob todeploy.needson the dr-freshness/load-freshness pattern (bounds the blind window without paying for three engines per deploy), or promote firefox/webkit back into the gate. Expected: §22 mat 3→4.
- Remediation: either add a
Cross-repo comparison
How Store relates to MMCA.Common (the framework) and MMCA.ADC (the sibling consumer) is maintained once, for all three repos, in the workspace-internal Docs/Architecture/CrossRepoComparison.md (not published).