Architecture governance
MMCA.Common: Architecture Scorecard
Canonical, version-controlled scorecard for this repo: the single source of truth for MMCA.Common's architecture scores (replaces the former single-axis snapshot; see git history). Scored against the rubric at
ArchitectureEvaluationCriteria.md; framework-wide facts (version, packages, ADR range, fitness counts) live inFACTS.md. The cross-repo Common-vs-ADC-vs-Store narrative lives once 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, scored fresh against the v2 criteria for the first time this cycle; §16 is AI-Native Application Architecture, N/A; Σweight 81→80) • Date: 2026-09-04 (thirty-second wave: full 34-category, two-pass evidence re-score at framework v1.185.0, git HEAD 7018a48, clean working tree; seventeen published packages and 124 fitness methods across 47 bases (Common itself runs 197) per FACTS.md, ADR set 001-110 per the ADR index) • Two scores move, both on the Implementation axis and in opposite directions. §31 Cost Efficiency / FinOps Implementation 7→8 on new in-tree substance shipped since the prior wave: the default-ON Telemetry:FilterProbeTelemetry knob wires ProbeTelemetryFilter into both the ASP.NET Core and HttpClient tracing instrumentation (Aspire/Extensions.cs:224), and a ProbeTelemetryFilterProcessor registered before the exporters un-records the probe-child dependency spans the inbound filter cannot reach, the health check's SQL SELECT 1 and the Redis PING (Extensions.cs:248, Aspire/Telemetry/ProbeTelemetryFilterProcessor.cs:20); it is covered by 27 CI-gated tests in MMCA.Common.Aspire.Tests (MMCA.Common.slnx:46) and shipped in v1.182.0, the release the CHANGELOG titles "Cost release" (CHANGELOG.md:136), with v1.181.0 first making the Telemetry:DisableHttpClientMetrics and DisableRuntimeMetrics toggles actually hold under UseAzureMonitor() by registering MetricStreamConfiguration.Drop views (CHANGELOG.md:187). That falsifies the "byte-identical evidence, newest §31 artifact is the v1.118.0 knob pair" ground of the four prior refutations. Held at 8, not 9, and Maturity stays 2: right-sizing and reversible scale-events are still consumer-side actions the framework only recommends (common-COST.md:69), cost attribution and budgets exist only as compile-only sample IaC (samples/deployment/main.bicep:166), and Tests/Architecture still returns zero cost/budget/FinOps matches. §7 Microservices Readiness Implementation 9→8, a rubric-change deduction rather than the band recalibration eight prior cycles declined: rubric v2 added the criterion "Modernization patterns named: an Anti-Corruption Layer wraps any legacy or third-party model at the boundary, and extracting a module ... follows the Strangler Fig route" (ArchitectureEvaluationCriteria.md:263, added by ADR-110:55), and neither term appears anywhere in MMCA.Common source, docs, or any ADR. The ACL exists unnamed as an enforced convention (the hand-written adapter that implements the C# interface contract and delegates to the typed gRPC client, Grpc/DependencyInjection.cs:58), and ADR-008 documents a cutover ("Delete the combined MMCA.ADC.WebAPI host", 008-service-extraction-topology.md:33), not the strangler route. The other seven §7 criteria are met at high quality, so the row sits at the top of the Strong band, and Maturity 4 is untouched because the extraction boundary is a live merge gate (Rules/Layering/ArchitectureRules.Transport.cs:19, Bases/Layering/MicroserviceExtractionTestsBase.cs:13, MicroserviceExtractionTests.cs:11, MMCA.Common.slnx:52, ci.yml:144). Four first-pass lifts came back FLAG and are held at prior by merged-prior policy (in every case the prior is also what the fresh evidence supports): §12 8→9 (load/stress and evidence-based capacity are structurally deferred to consumers, backpressure is only broker defaults, nothing new since 2026-07-24); §13 8→9 (main.bicep:155 still comments "SLO alerting" while :156 declares only a Microsoft.Insights/actionGroups resource, no metricAlerts, and the only runbook remains the restore drill); §17 3/8→4/9 (no §17 evidence changed: the validate job is compile-only and is not one of the required contexts, and main.bicep:143 still binds secretRef: 'sql-conn' with no secrets: array); §30 3/8→4/9 (zero §30 substance between v1.179.0 and HEAD, every privacy-path diff a rename, and no PII/privacy/DSAR entry across the v1.180-v1.185 CHANGELOG: the eighth consecutive refusal by the ledger's own count). §10 Messaging & Integration Architecture is scored fresh under its v2 name for the first time and comes back CONFIRMED at M4/I9, so the "carried at prior scores pending re-score" caveat retires: broker topology by ADR with BrokerEventBus writing to the outbox and BrokerMessageBus publishing to RabbitMQ or Azure Service Bus, exponential UseMessageRetry over second-level delayed redelivery (Infrastructure/DependencyInjection.cs:961,957), fault-consumer plus dead-letter retention and ReplayDeadLettersAsync, an EF inbox staged in the handler's own transaction, upcasting contract evolution under ADR-090 with fitness-enforced versioning, and ADR-086's documented saga deferral with the ADR-054 choreography alternative. Anchor corrections: §7's two fitness files moved into Rules/Layering/ and Bases/Layering/, MicroserviceExtractionTests.cs declares at :11 not :10, and the inbox idempotency check is Infrastructure/Messaging/Consumers/IntegrationEventConsumer.cs:54, not Infrastructure/Services/IntegrationEventConsumer.cs:42; §31's row cited a COST.md that no longer exists in MMCA.Common and now cites common-COST.md; §13's SLO-alerting anchor is main.bicep:155 (the comment) against :156 (the action group). Indices: Maturity 97.5% (312/320, unchanged) and Implementation 86.3% (690/800, from 86.4%: +2 for §31 on weight 2, −3 for §7 on weight 3). The prior thirty-first-wave header (2026-09-01 at v1.179.0, git HEAD 84116a3, clean tree: no score moves, eight first-pass lifts refuted and held at prior) is condensed here and recorded in the Indices block and git history. Recalibration (2026-08-01): Implementation 10 is awardable for an almost perfect implementation, so the former "attainable ceiling" reporting line is retired and the index reads directly against 100% (denominators unchanged, so the trend line stays comparable). • Two axes per category: Maturity (0-4, process/governance) and Implementation (0-10, substance/execution). Indices computed deterministically from the scores below. Historical wave-by-wave detail lives in the executive summary below and in git history.
Update 2026-06-30 (tenth wave: focused in-repo remediation). Four scores moved up on shipped, tested evidence and both indices rose: Maturity 91.7% → 92.9% (301/324) and Implementation 84.1% → 84.9% (688/810). The moves: §27 i18n Maturity 2→3 / Implementation 6→7 (a ResourceTranslationsAreComplete translation-coverage fitness gate plus MA0076 raised to a build error close the two ADR-027 §7 follow-ups; 33 culture-less formatting sites fixed); §29 Resilience Maturity 3→4 (the in-repo DatabaseRestoreDrillTests is a build gate run on every CI build, now with a recorded measured RTO/RPO baseline in RESILIENCE.md; a build gate, not a non-gating scheduled cron, is the M4 bar); §28 Front-End Testing Implementation 8→9 (a deterministic render-snapshot regression tier, MarkupSnapshot + PrimitivesSnapshotTests, shipped in Testing.UI for reuse); §21 Accessibility Implementation 8→9 (axe coverage broadened to the loading/error states plus a documented manual screen-reader pass in ACCESSIBILITY.md, which also found and fixed a real WCAG 4.1.2 defect in PageLoadingState). One honest finding logged but not gated: a dark-mode palette contrast gap (filled-primary button label + error-alert text), tracked under §20. Fitness counts advanced to 72 methods / 19 bases (Common runs 39) per the regenerated FACTS.md. The dated note above records the prior ninth-wave (v1.92.0) re-score; the figures in the executive summary and closing analysis below predate this wave (the Indices section is authoritative).
Update 2026-06-30 (eleventh wave: ADR governance for two pre-existing mechanisms). No score moves this cycle. A full 34-category evidence re-score at framework v1.93.0 (git HEAD 3e72bfa, working tree dirty, see the caveat below) re-confirmed every category at its tenth-wave value, so the indices hold at Maturity 92.9% (301/324) / Implementation 84.9% (688/810) (the Indices section remains authoritative). The wave is governance and docs only: two new ADRs record mechanisms that already ship in framework code. ADR-033 (resource-ownership authorization) documents the OwnerOrAdminFilter plus OwnershipHelper axis (a single-resource 403 filter keyed on the customer_id claim, plus an ownership Specification that row-scopes collection queries, with one admin bypass; opt-in per controller, claim-trusting, not ABAC, adopted in MMCA.Store). ADR-034 (generic entity controllers + dynamic query contract) documents the EntityControllerBase / AggregateRootEntityControllerBase generic REST surface and OData-lite query contract (sparse fields, per-type IFilterStrategy filtering, sort, pagination with X-Pagination, the MaxUnboundedResultLimit ceiling, two-path includes). Both enrich evidence (§11, §9/§2/§12, §34) without moving a score: ADR-033 is the resource-ownership criterion §11's Implementation-8 cap named, but it stays ownership-by-claim, opt-in, not-ABAC, and Store-adopted, so §11 correctly holds at Implementation 8. The source-generated, CI-gated FACTS.md advanced to ADR set 001-034 and 72 fitness methods across 19 bases (Common runs 39); this scorecard's prose is synced to match. Honest caveat: the tree is dirty (ADRs 033/034 added; ADRs 015/026/030 plus ADRs/README.md and FACTS.md modified) until this docs pass is committed, the recurring per-cycle §34 hygiene nit, tracked in the backlog.
Update 2026-06-30 (twelfth wave: under-8 Implementation lift, v1.94.0 pending release). Two Implementation scores move up on shipped, tested in-repo evidence; both indices' Implementation axis rises while Maturity holds: Maturity 92.9% (301/324) unchanged, Implementation 84.9% → 85.3% (691/810). The moves: §22 Responsive & Cross-Browser Implementation 7→8, the two execution gaps the prior 7 named are closed with real framework code: grid density options now ship on DataGridListPageBase (DenseGrid + ToggleDensity(), persisted through ListPageState/URL/sessionStorage, unit-tested) and the 48px touch target is generalized from a cart-drawer-only rule into a shared .mmca-touch-target affordance (cart drawer + mobile cards + pager, enforced by a phone-viewport Playwright bounding-box test that passes locally), plus a documented RESPONSIVE.md device/browser matrix; and §27 Internationalization Implementation 7→8: a real pseudo-localization pass ships (PseudoLocalizer transform + an IStringLocalizerFactory decorator activated by a Development-only qps-Ploc culture, inert in production, unit-tested), closing the pseudo-localization gap the prior 7 named. §31 FinOps stays at Implementation 7 by deliberate, documented acceptance (a structural cap, not an open gap): its two unmet criteria (right-sizing and reversible scale-events) are consumer/IaC execution a NuGet library provisions nothing to perform, and per-service cost attribution via Aspire annotations is inert for the hand-written-Bicep consumers (ADC/Store); the in-repo levers (Telemetry:TracesSampleRatio, outbox-log trim, COST.md) are already shipped. Full Common Release build clean, 1685 tests pass; the touch-target + pseudo-loc tests are green. The version is v1.94.0 pending the release tag + consumer sweep (held for review at this writing, so the working tree is dirty against the v1.93.0 tag).
Update 2026-07-02 (thirteenth wave: performance-smoke CI gate + rubric-band recalibration). A full 34-category evidence re-score at framework v1.99.0 (git HEAD 4b83891, working tree dirty, see the caveat below) moves five scores: one maturity up, four implementation recalibrated down to their rubric bands. §12 Performance & Scalability Maturity 3 to 4: the BenchmarkDotNet hot-path harness is now a committed CI gate (a performance-smoke job runs the benchmarks --filter "*" --job Dry on every push/PR, .github/workflows/ci.yml:149), so the §12 hot path (Specification compiled-expression cache + criteria composition) is automatically enforced, meeting the rubric's maturity-4 "enforced automatically (CI), documented, evolved deliberately" bar. Implementation holds at 8 because that CI benchmark is a runs-clean smoke, not a latency-regression gate (ci.yml:145), and load/stress timing data still lives in the consumer apps. §14 Testability 10 to 9, §19 State Management 9 to 8, §26 Front-End Security 9 to 8, §34 Governance & Docs 9 to 8: four recalibrations to the Strong band on the same evidence the prior scores hedged as "just shy of 10". The 53% coverage floor is modest and there is no mutation testing (§14), server-vs-client-state staleness is only partially deliberate (§19), the default static CSP omits script-src/style-src (§26), and the governance docs' own uncommitted-ADR pass keeps traceability momentarily inconsistent with the CI-gated FACTS.md (§34). This is honest calibration to the rubric bands, not regressed work. Net indices: Maturity 92.9% to 93.5% (301 to 303 / 324, +2 from §12), Implementation 85.3% to 84.0% (691 to 680 / 810, minus 11 from the four Implementation recalibrations: §14/§19/§26 each minus 3 on weight 3, §34 minus 2 on weight 2). Implementation stays the weaker axis and the gap widens to ~9.5 points (Maturity up, Implementation recalibrated down); no category is N/A. The source-generated, CI-gated FACTS.md advanced to fourteen packages / ADR set 001-036 / 77 fitness methods across 24 bases (Common runs 39) and this scorecard's prose is synced to match. Honest caveat: the tree is dirty (a docs pass is uncommitted: ADRs 035/036 added and untracked; ADRs 015/023/027/029/030/032/033 + ADRs/README.md + FACTS.md modified), the recurring per-cycle §34 hygiene nit, tracked in the backlog. The Indices section below is authoritative; the executive summary and closing analysis predate this wave.
Update 2026-07-03 (fourteenth wave: clean-tree evidence re-score at v1.101.0). A full 34-category, two-pass evidence re-score (scorer plus adversarial verifier per category) at framework v1.101.0 (git HEAD 5e55be2, working tree clean: the recurring uncommitted-docs-pass caveat is closed this cycle) moves two scores. §24 Forms, Validation & UX Safety Maturity 3 to 4: the shared auth-form convention is enforced automatically in the CI-gated .slnx unit tier (AuthModelValidationTests, 8 facts covering required/email/complexity/password-match, plus RegisterFormTests asserting the per-field ValidationMessage renders on an empty submit with the auth service never invoked), meeting the rubric maturity-4 "enforced automatically by tests/CI" bar, consistent with how §19 holds M4 on the same CI-gated guard tests; the shared FormsConventionTestsBase fitness base remains consumer-scoped (it enumerates Source/Modules/**/*Create.razor, which Common does not contain, and Common has no subclass), so the in-repo enforcement rests on those bUnit/model tests. §14 Testability Implementation 9 to 8: an internal-consistency band recalibration, not regressed work; the row's own stated reasoning (a modest 53% gated coverage floor, no mutation testing) describes the rubric's Strong band (7-8) while 9 sits in Exemplary, so 8 is the number that matches the reasoning. Four further first-pass downgrade proposals were adversarially reviewed and not applied: §25 (Implementation 8 to 7) and §34 (Maturity 4 / Implementation 8 to 3/7) were refuted on evidence, and §7/§9/§13 (each Implementation 9 to 8, a band recalibration) were declined on review, so all five hold at their prior scores; their rows receive evidence corrections only (drifted Aspire/Extensions.cs and Directory.Packages.props line anchors, the extraction rule body relocated to the shared Testing.Architecture package, and one stale §13 hold-reason: the warm-up subsystem HAS been unit-tested since the eighth wave, so the §13 hold at 9 rests solely on deployer-owned SLO alerting/dashboards/runbooks). The stale §34 dirty-tree caveat is removed: ADRs 035/036 and the docs pass are committed, so governance traceability is consistent with the CI-gated FACTS.md again. Net indices: Maturity 93.5% to 94.1% (303 to 305 / 324, +2 from §24), Implementation 84.0% to 83.6% (680 to 677 / 810, minus 3 from §14 on weight 3). Implementation stays the weaker axis (gap ~10.6 points); no category is N/A. The Indices section below is authoritative; the executive summary and closing analysis predate this wave.
Update 2026-07-03 (fifteenth wave: i18n completion train, §27 Maturity 3 to 4 + Implementation 8 to 9). The ADR-027 amendment (Decision 9) closes every stated §27 hold. Maturity 3 to 4: the pseudo-localization pass is no longer a developer diagnostic: PseudoLocalizationE2ETests runs in the REQUIRED chromium ui-e2e job (the gallery host enables qps-Ploc unconditionally as unpackaged test infrastructure) and fails the merge on a missing [!! sentinel (resource round-trip) or horizontal overflow under the ~40% expansion (the rubric's layout-tolerance criterion, previously unevidenced), alongside an en-US leak guard; a second fitness gate (LocalizedTextConventionTestsBase, subclassed in-repo as LocalizedTextConventionTests) fails the build on NEW hard-coded snackbar/title/<PageTitle>/breadcrumb/NavItem literals, so the localized state is machine-enforced end to end, not review-enforced. Implementation 8 to 9: MudBlazor's built-in chrome now localizes (ResxMudLocalizer over MudTranslations.{resx,es.resx}, all 145 built-in keys en + es, DI-resolution-tested), the framework's own UI chrome is fully externalized (NavMenu, auth pages, error/empty/loading states, ReconnectModal, notification pages, the UI.Web SSR Error page: SharedResource grew 22 to 136 keys), the ErrorMessages.Success fragment-concatenation red flag is [Obsolete] (whole-sentence page keys replace it) and the Common.Error.* templates no longer surface raw ex.Message, and NavItem.TitleResource makes module nav menus culture-aware. Held below 10: two locales, no RTL. LocalizationResourceTests also gains a non-vacuous floor (MinimumBaseResources = 3). Net indices: Maturity 94.1% to 94.4% (305 to 306 / 324), Implementation 83.6% to 83.7% (677 to 678 / 810), both +1 on §27's weight 1.
Update 2026-07-06 (sixteenth wave: clean-tree re-score at v1.106.0, one score moves). A full 34-category, two-pass evidence re-score (per-category scorer plus adversarial verifier) at framework v1.106.0 (git HEAD 6f8b917, one commit past the v1.106.0 tag, working tree clean) moves one score. §14 Testability & Test Strategy Implementation 8 to 9: the specific reason the fourteenth wave recalibrated §14 down to 8 (a "modest 53% gated coverage floor") is resolved. The unit-tier coverage floor was ratcheted 53.0 to 68.3 (.github/workflows/ci.yml:226, commit b75fa8f, measured ~70.3% after the C1-C4 coverage program), and the suite grew from 1586 to 1880 [Fact]/[Theory] across 262 files via a coverage-driven program that found and fixed seven real defects (C-1..C-7, 55f3cab), coverage tracked on meaningful paths rather than as a vanity number, so the top band is now supported. Maturity holds at 4 (CI-gated fitness tests plus the blocking coverage-floor gate plus the zero-discovery min-tests guard); it is held below 10 only by the one remaining Exemplary gap, no mutation testing on the Core tier. All 33 other categories re-confirmed at their prior scores against current source: the defect-fix wave C-1..C-7 (§11 lockout-backoff-overflow clamp, §13 LoggingQueryDecorator query-failure outcome=failed RED-metric parity, the OAuth returnUrl safe-lookup, ChildEntityServiceBase token attach) and ADRs 037 (field-level encryption at rest) plus 038 (supply-chain provenance) are all evidence enrichment in categories already scored 8-9. ADR-037's EncryptedStringConverter is explicitly latent/unadopted, and ADR-038 records the SBOM as "generated, not yet signed or attested", so none moves a score. §34 Governance & Docs holds at M4/I8: the adversarial pass proposed 8 to 7 because the scorecard's own prose was stale against the CI-gated FACTS.md (it self-dated v1.101.0 / ADRs 001-036 while FACTS.md reports v1.106.0 / 001-038), but this refresh cures that staleness (the rewritten scorecard is current at commit time), and the durable Strong-8 cap remains the uncommittable ArchitecturalAnalysis.md in the workspace root. The source-generated, CI-gated FACTS.md is now ADR set 001-038 / 78 fitness methods across 25 bases (Common runs 40) and this scorecard's prose is synced to match. Net indices: Maturity 94.4% (306/324) unchanged, Implementation 83.7% to 84.1% (678 to 681 / 810, +3 from §14 on weight 3). Implementation stays the weaker axis (gap ~10.3 points); no category is N/A. The Indices section below is authoritative; the executive summary and closing analysis predate this wave.
Update 2026-07-09 (seventeenth wave: evidence re-score at v1.108.0, one score recalibrated). A full 34-category, two-pass evidence re-score (per-category scorer plus adversarial verifier) at framework v1.108.0 (git HEAD 6c3b3bc, working tree clean, one commit ahead of origin: the ADR-012 mixed-endpoint transport amendment awaiting push) moves one score. §21 Accessibility Implementation 9 to 8: a band recalibration on evidence already on the record, not regressed work. The shipped, user-toggleable dark theme (ADR-028) carries two documented, deliberately un-gated WCAG AA contrast failures (the filled-primary button label and the error-alert message text, ACCESSIBILITY.md known limitations), a directly unmet §21 color-and-contrast criterion; the prior 9 parked that gap under §20 while sitting in the rubric's Exemplary band ("all criteria met to a high standard"), which does not hold when a §21 criterion fails in a default-available feature. Alongside the two standing smaller gaps (no automated focus-trap/reading-order assertion, manual pass only; axe breadth scoped to the gallery's representative states), the category recalibrates to the top of Strong. Held at 8 rather than 7 (reviewed): the gaps read as one minor cluster whose contrast half is a single palette-tuning item already tracked as the open §20 backlog entry, and everything else §21 claims is intact (the required chromium axe merge gate over the WCAG 2.1 AA tag set including the loading/error states, semantic landmarks and named controls, the documented manual screen-reader pass, the shipped E2E a11y bases). Maturity holds at 4. Two adversarially-refuted first-pass proposals are recorded, not applied: §8 Implementation 8 to 9 refuted (the rubric's "migrations run in CI/CD" criterion is structurally unmet in-repo: ci.yml has no migration-apply step, and ADR-030's boot-time self-migration is a runtime mechanism, not that gate) and §22 Implementation 8 to 9 refuted (no repo change since the twelfth-wave lift; firefox/webkit remain advisory continue-on-error, so the chromium-only gate stays the standing §22 gap). All 33 other categories re-confirmed at their prior scores against current source. The ADR set grew to 001-039 (ADR-039 live channel push: ephemeral events over the notification hub via ILiveChannelPublisher, plus the ADR-012 mixed-endpoint transport-profile amendment), both evidence enrichment in categories already scored 8-9 with no score move. The source-generated, CI-gated FACTS.md reports 78 fitness methods across 25 bases (Common runs 41) and this scorecard's prose is synced to match. Net indices: Maturity 94.4% (306/324) unchanged, Implementation 84.1% to 83.7% (681 to 678 / 810, minus 3 from §21 on weight 3). Implementation stays the weaker axis (gap ~10.7 points); no category is N/A. The Indices section below is authoritative; the executive summary and closing analysis predate this wave.
Update 2026-07-12 (eighteenth wave: evidence re-score at v1.115.0, three scores move). A full 34-category, two-pass evidence re-score (per-category scorer plus adversarial verifier) at framework v1.115.0 (git HEAD 37d0a3b, working tree clean, at the release tag) moves three scores, all front-end. §20 Design System Implementation 8→9 and §21 Accessibility Implementation 8→9: the two documented dark-theme WCAG AA contrast failures that capped both categories (filled-primary button label + error-alert message text) are fixed in the palette (MMCATheme.cs:60,73, dark PrimaryContrastText/ErrorContrastText = rgba(0,0,0,0.87)) and locked by a blocking dark-mode axe gate (DarkModeE2ETests in the required chromium ui-e2e job, ci.yml:114); this is exactly the "tune the dark palette and gate the dark-mode axe scan" path back to 9 the seventeenth wave named. §20 stays below 10 on the residual raw-hex/!important in app.css and the Bootstrap-chrome coexistence; §21 stays below 10 on the manual-only focus-trap/reading-order pass. §23 Front-End Performance Maturity 3→4: the front-end performance conventions the thirteenth wave recalibrated to review-enforced are now measured AND automatically enforced: WebVitalsE2ETests asserts LCP/TTFB/CLS budgets on the gallery Login + Components pages inside the same blocking chromium ui-e2e gate (WebVitalsE2ETests.cs:43, ci.yml:114,145), meeting the rubric's maturity-4 "enforced automatically (CI)" bar; Implementation holds at 8 on the retained desktop-grid render-efficiency gap. Five adversarially-refuted first-pass proposals are recorded, not applied: §7 Implementation 9→8 (an unforced band recalibration re-litigating a fourteenth-wave decline), §10 Implementation 8→9 (the three documented hold-reasons still in source), §25 Maturity/Implementation downgrade (every §25 mechanism present on a clean tree), §26 Implementation 8→9 (the CSP script-src/style-src gap is unclosed), and §34 Implementation 8→7 (a transient stale-prose basis this refresh cures) all hold at their prior scores. All 26 other categories re-confirmed at their prior scores. Six capabilities landed since v1.108.0 as evidence enrichment with no band move: ADR-040 authenticated output caching, ADR-041 observability/telemetry, ADR-042 MAUI device-capability abstraction (the fifteenth package MMCA.Common.UI.Maui), ADR-043 mobile deep links + native OAuth callback, ADR-044 native push delivery, ADR-045 managed file storage + avatars, all in categories already scored 8-9 (§18/§6/§8/§11/§30). The source-generated, CI-gated FACTS.md reports 15 packages / ADR set 001-045 / 85 fitness methods across 28 bases (Common runs 46) and this scorecard's rows are synced to match. Net indices: Maturity 94.4% → 95.1% (306 → 308 / 324, +2 from §23 on weight 2), Implementation 83.7% → 84.3% (678 → 683 / 810, +2 from §20 on weight 2, +3 from §21 on weight 3). Implementation stays the weaker axis (gap ~10.8 points); no category is N/A. The Indices section below is authoritative; the executive summary and closing analysis predate this wave.
Update 2026-07-15 (nineteenth wave: evidence re-score at v1.115.0, three table moves, one honest downgrade). A full 34-category, two-pass evidence re-score (per-category scorer plus adversarial verifier) at framework v1.115.0 (git HEAD c7b3cb9, working tree dirty: an ADR-adding pass is in flight, ADRs 046-048 untracked plus ten modified files including FACTS.md) moves three table scores. §22 Responsive & Cross-Browser Maturity 3→4: firefox was promoted to a required merge gate alongside chromium on 2026-07-12 (ci.yml:112, with continue-on-error now scoped to webkit only, ci.yml:115), exactly the promotion path the prior risk list named for +1 maturity; two real engines now block merges, clearing the chromium-only red flag (Implementation holds at 8: the promotion is an enforcement move, not new responsive execution breadth, and webkit stays advisory). §12 Performance & Scalability Maturity 4→3 (downgrade): the sole basis for the thirteenth-wave M4 was the performance-smoke CI job as automatic enforcement, but that job is a runs-clean smoke (--job Dry, fails only if a benchmarked path throws or no longer compiles, ci.yml:172,193), not a latency-regression gate, so it does not automatically enforce the performance property the rubric's maturity-4 bar requires; convention plus review plus a compile/run smoke is Maturity 3 (Implementation holds at 8; no work was lost, the recalibration rests entirely on the smoke-vs-regression distinction). §23 Front-End Performance Maturity 3→4 applied to the table: the eighteenth-wave prose recorded this move but the table row and the Indices block were never updated (a partial-application inconsistency this refresh cures); the WebVitals budget gate (WebVitalsE2ETests.cs:43, blocking chromium ui-e2e job) is re-confirmed against source. Three adversarially-refuted first-pass proposals are recorded, not applied: §9 Maturity 3→4 (the in-repo OpenAPI drift gate is deliberately consumer-owned, OpenApiEndpointExtensions.cs:12), §25 Maturity 3→4 (NavigationFlow.md has no drift gate and the Routes.razor route-auth wiring has no test; only leaf behaviors are auto-tested), and §33 Implementation 8→9 (the new consumer-source-build cross-repo canary, ci.yml:262, is real progress but advisory (continue-on-error: true) and does not remove the two cited caps: the Aspire AppHost headless hang and Common not being independently runnable). All 28 other categories re-confirmed at their prior scores. ADRs 046-048 (HTTP API versioning, soft-deleted user session revocation, primitive identifier type aliases) land as evidence enrichment in categories already scored 8-9 with no move. The source-generated, CI-gated FACTS.md reports 15 packages / ADR set 001-048 / 85 fitness methods across 28 bases (Common runs 46) and this scorecard's rows are synced to match. Net indices: Maturity 94.4% → 95.1% (306 → 308 / 324: §22 +2, §23 +2, §12 -2), Implementation 84.3% (683/810) unchanged (the prior Indices block read 678/83.7%, stale against its own table rows after the eighteenth-wave §20/§21 lifts; also cured here). Implementation stays the weaker axis (gap ~10.8 points); no category is N/A. The Indices section below is authoritative; the executive summary and closing analysis predate this wave.
Update 2026-07-17 (twentieth wave: evidence re-score at v1.117.0, four scores move). A full 34-category, two-pass evidence re-score (per-category scorer plus adversarial verifier) at framework v1.117.0 (git HEAD 76d70cf, working tree clean) moves four scores. §25 Navigation & IA Maturity 3→4: the navigation-contract drift gate the prior risk list prescribed now ships and gates merges: NavigationContractTests reflects over every routable MMCA.Common.UI page and asserts set-equality plus auth-posture agreement against the embedded NavigationFlow.md routes table (with a non-vacuous 8-route floor), and the test project sits in MMCA.Common.slnx, so the gate runs in the CI-gated unit tier (NavigationContractTests.cs:29,44, MMCA.Common.slnx:45), meeting the rubric maturity-4 "enforced automatically (CI)" bar and closing the §25 red flag (navigation drifting from the documented per-role design). Implementation holds at 8: the gate is an enforcement move, not new execution breadth, and the "deep-link param typing beyond list-state is still light" cap is unchanged (framework routes are plain string templates with only the sanitized ?returnUrl= query state), the same calibration §22/§23 applied to their own gate promotions. §33 Developer Experience Maturity 3→4 + Implementation 8→9: the consumer-source-build canary (builds MMCA.Helpdesk against the PR's framework source via UseLocalMMCA) was promoted from advisory to a required merge gate on 2026-07-16 (continue-on-error removed and the "Consumer source build (Helpdesk)" context added to branch protection after 9 consecutive green runs, ci.yml:267-268, confirmed live via the branch-protection API), so a framework change that breaks a source-mode consumer now blocks the merge automatically: that clears both the maturity-4 automatic-enforcement bar and the sole cap the nineteenth wave placed on Exemplary implementation. Held below 10 by the residual friction items, now implementation notes only (the Aspire AppHost hangs when launched headless, and Common itself is a library, not independently runnable). §22 Responsive & Cross-Browser Implementation 8→9: webkit was promoted to a required merge gate on 2026-07-16 after 11 consecutive green main runs (ci.yml:112-114, no continue-on-error remains anywhere in the job, confirmed live via the branch-protection API), so all three engines now block merges: this closes the one stated reason the row held at 8 ("webkit remains advisory") and completes the full 3-engine matrix as an enforced, not just exercised, capability. Held below 10: the matrix runs against the backend-less gallery's representative states, and RESPONSIVE.md's device rows remain a documented matrix, not device-lab execution. (Doc drift cured with this refresh: RESPONSIVE.md still listed webkit as advisory.) §13 Observability Implementation 9→8 (band recalibration, no work lost): the row's own text names two of the six §13 criteria as deployer-owned (SLO alerting/dashboards and runbooks), which is the rubric's Strong band ("nearly all criteria met, minor isolated gaps"), not the Exemplary "all criteria met to a high standard" that 9 claims: the same calibration this scorecard already applies to §17 and §29 for structurally identical deferred-to-consumer reasoning. The in-repo substance is re-confirmed and unchanged (warm-up readiness gate wired unconditionally into AddServiceDefaults with 9 unit tests, RED-metric command/query parity, poll-span noise filtering; stale row anchors corrected to Extensions.cs:38,173,188,302-315,344-352). Three first-pass lift proposals were adversarially refuted and are recorded, not applied: §9 Maturity 3→4 (the OpenAPI/contract drift gate is deliberately consumer-owned per OpenApiEndpointExtensions.cs:13, and no §9-specific CI gate exists in-repo, so enforcement stays review/convention), §17 Maturity 3→4 (the new sample-deployment-validate job compile-checks the reference Bicep on every push, ci.yml:311-322, but a real what-if/deploy stays consumer-side by the workflow's own comment, so the CD axis is not automatically enforced in-repo), and §31 Maturity 2→3 / Implementation 7→8 (the proposal re-cited the identical evidence already grounding M2/I7; no new §31 work exists). One recorded candidacy is declined on live branch-protection evidence: §12 stays Maturity 3. The backlog's latency-regression gate shipped 2026-07-16 (the performance-smoke job now runs --job Short --exporters json and build/perfgate fails on committed perf-baseline.json ceiling/ratio violations, ci.yml:174-200), a real qualitative upgrade over the runs-clean smoke, but the job's context is absent from the live required_status_checks list (queried via the branch-protection API; CONTRIBUTING.md:57-62 agrees), so a red perf gate does not block a merge and the rubric's merge-gate bar is unmet; adding the context to branch protection is now the whole path to M4. All 28 other categories re-confirmed at their prior scores. The source-generated, CI-gated FACTS.md reports 15 packages / ADR set 001-048 / 85 fitness methods across 28 bases (Common runs 49) and this scorecard's rows are synced to match (the prior "Common runs 46" fitness-subclass count was stale against FACTS.md). Net indices: Maturity 95.1% → 96.3% (308 → 312 / 324: §25 +2, §33 +2), Implementation 84.3% → 84.6% (683 → 685 / 810: §22 +2, §33 +2, §13 -2). Implementation stays the weaker axis (gap ~11.7 points); no category is N/A. The Indices section below is authoritative; the executive summary and closing analysis predate this wave.
Update 2026-07-21 (twenty-first wave: evidence re-score at v1.121.0, one score moves). Recorded in full in the executive summary below: §12 Performance & Scalability Maturity 3→4 (the Performance gate (BenchmarkDotNet Short + baseline verify) context is present in live required_status_checks on main, 8 required contexts, refuting the twentieth wave's sole stated basis for holding it at 3). Maturity 96.3%→96.9% (312→314/324); Implementation unchanged at 84.6% (685/810). Seven proposed lifts (§4, §9, §11, §19, §20, §26, §29) were refuted on an adversarial second pass and held at prior; the remaining 26 categories re-confirmed with no move. One documentation drift (the CONTRIBUTING.md gate list vs live protection) was routed to the backlog under #34 and fixed the same day (MMCA.Common PR #100).
MMCA.Common is a .NET 10.0 (LangVersion: preview) framework shipped as seventeen NuGet packages per FACTS.md to both nuget.org and GitHub Packages (dual-registry since v1.128.0, ADR-053) (Shared, Domain, Application, Infrastructure, API, Grpc, Gateway, UI, UI.Maui, UI.Web, Aspire, Aspire.Hosting, Testing, Testing.E2E, Testing.UI, Testing.Architecture, plus the MMCA.Common metapackage) (a library/SDK, not a runnable app) that supplies the cross-cutting foundation for modular-monolith applications built on DDD, Clean Architecture, and CQRS. Its substance is dense and consistent: a Result<T> error-flow pattern, soft-delete/audit-stamping aggregate base classes, an auto-discovered IModule system with topological (Kahn) registration, a Scrutor decorator pipeline, EF Core with a database-per-microservice resolver, and an outbox for cross-source consistency, all layered across Core/Presentation/Hosting tiers. The headline finding is that architecture rules are not aspirational here: the dependency rule is enforced twice over (compile-time MSBuild targets in §3 plus NetArchTest fitness functions, now factored into a define-once shared rule library: the 13th package Testing.Architecture: consumed identically by all three repos per ADR-015), and the same governance rigor extends to microservice-extraction boundaries (§7), supply chain (§32, the MassTransit-v8 license pin is a build gate, now also ADR-016), security (§11), and docs (§34's now-sixteen dated ADRs, 001-016). This is a reference-quality governance posture. The ADR set has since grown to twenty-two (001-022), a polyglot-persistence capability (three storage engines behind one entity model) ships as tested framework plumbing (ADR-018), v1.80.0 adds an opt-in permission-based authorization layer plus TimeProvider adoption (fourth wave), and v1.81.0-v1.82.0 add an opt-in Scalar OpenAPI UI, a FinOps COST.md, an RS256 pin on the JWKS-forwarded auth path, and three newly-committed ADRs (020-022) that clear the prior cycle's governance nits (see the fifth wave below).
This re-verification (2026-06-20) found the prior report materially stale. A remediation wave landed on 2026-06-19/06-20 (sixteen commits between v1.68.0 and v1.72.0) that closes four of the previously-flagged top risks, each verified against source: (1) event-schema versioning now exists: BaseIntegrationEvent.SchemaVersion => 1 (ADR-010) plus an EventVersioningConventionTests fitness gate, removing the §6 cross-service contract-break risk; (2) OpenAPI generation now ships: AddCommonOpenApi()/MapCommonOpenApi() over the built-in Microsoft.AspNetCore.OpenApi 10.0.8, removing the §9 "undocumented specs" red flag; (3) the CI coverage gate is now enforced: ci.yml fails the build below a 53% line-coverage floor (was report-only), closing the last §14 gap; (4) the chaos/fault-injection test is now real: ResilienceCircuitBreakerFaultInjectionTests trips a breaker and asserts short-circuiting (was prose-only TODO), lifting §29. Two further hardening moves landed: the CycloneDX SBOM step is now a blocking release gate (was continue-on-error), and the ADR set grew 9→11 (ADR-010 event versioning, ADR-011 single-locale i18n), each with a matching fitness function.
A second wave landed 2026-06-21/06-22 (v1.73.0→v1.76.0), folded into this re-verification. It is governance-and-consolidation rather than score-moving substance, so the scores below are unchanged, but it reinforces the already-high §14/§15/§32/§34 ratings: (1) the architecture fitness functions were extracted into a define-once shared package: MMCA.Common.Testing.Architecture (the 13th package, v1.73.0) holding one IArchitectureMap-parameterized NetArchTest rule library + abstract test bases, so Common, ADC, and Store now run literally the same rules (ADR-015) instead of parallel copies; (2) shared cross-cutting infrastructure was promoted up into Common (v1.74.0, drift-reduction P4); (3) the ADR set grew 11→16: ADR-012 (gRPC-host transport), ADR-013 (Result pattern), ADR-014 (CQRS decorator pipeline), ADR-015 (architecture fitness functions), ADR-016 (lockstep versioning + MassTransit-v8 pin): documenting patterns that previously lived only in code; v1.76.0 itself was a docs-only release. The consumer apps were swept to v1.76.0 in lockstep (and have since moved with the framework to v1.79.0: see the third wave below), so the framework and both consumers remain fully converged with no version lead.
A third wave landed 2026-06-23/06-24 (v1.77.0→v1.79.0), folded into this re-verification. It adds one capability and several governance refinements: (1) polyglot persistence (ADR-018, v1.79.0): three storage engines (SQL Server / Cosmos / SQLite) behind one entity model, selected per entity by a [UseDataSource] attribute on an engine shim base (EntityTypeConfigurationSQLServer/Cosmos/Sqlite over the engine-aware EntityTypeConfiguration<TEntity,TId>), with WithSQLServerDataSource/WithCosmosDataSource/WithSqliteDataSource AppHost helpers (Aspire.Hosting/Extensions.cs:132/166/198), a CrossSourceSpecification.BuildAsync helper for engine-portable cross-source filters, and a new opt-in SpecificationsDoNotNavigateToOtherEntities fitness rule (ArchitectureRules.Specifications.cs:24) that fails the build on a latent cross-engine navigation hazard. The plumbing is complete, unit/integration-tested, and shipped, but no production entity routes to a non-SQL engine yet (ADR-018 records the capability as deliberately latent, even ADC's Conference Session→Cosmos / Room→SQLite trial was built, tested, then reverted to all-SQL-Server with the extension points kept). (2) ADR-017 (request idempotency) now formally documents the long-standing [Idempotent] HTTP-edge filter (Source/Presentation/MMCA.Common.API/Idempotency/, present since v1.14.0: Idempotency-Key header → distributed-cache replay with X-Idempotent-Replay, per-key SemaphoreSlim double-check), the inbound counterpart to ADR-003's consumer idempotency. (3) Supply-chain governance deepened: a committed .github/dependabot.yml adds weekly NuGet/Actions update PRs with semver-major ignores for the held packages (MassTransit, VS.Threading.Analyzers, StackExchange.Redis, MessagePack), reinforcing the §32 pin discipline in config (not just the fitness test), plus a sweep of dependencies to latest stable. (4) A 985-line, 9-phase GETTING-STARTED.md adoption guide shipped (worked companion to MMCA.Helpdesk), materially strengthening onboarding (§33). Two minor §34 hygiene regressions accompany the wave: the CHANGELOG skips 1.72-1.78 (jumps 1.79.0 → 1.71.0), and ADR-018 is on disk but uncommitted at review time. Net score effect: §33 rises (M2→M3, I7→I8); all other categories hold, with enriched evidence.
A fourth wave landed 2026-06-25/06-26 (v1.80.0), folded into this re-verification. It is capability-and-governance enrichment that lands in categories already scored 9-10, so no numeric score moves this cycle, but the evidence under §11/§1/§4/§14/§34 is materially enriched, with three honest caveats recorded rather than papered over: (1) permission-based authorization now ships (commit bc6c5d7): IPermissionRegistry + PermissionRegistryBuilder (Shared) declare role→permission grants, [HasPermission("x")] resolves an on-demand perm:x policy via PermissionPolicyProvider, and PermissionAuthorizationHandler checks the caller's roles/claims against the registry; it is opt-in and backward-compatible (the four named role policies are untouched, the mechanism is inert until a host calls AddPermissions(...)), well-factored (immutable FrozenDictionary/FrozenSet registry, additive cross-module grants), and backed by 13 new unit tests. Caveat: this is RBAC with a role→permission capability indirection, not ABAC/resource-based authorization, so it is a real §11 "policy-based" win but should not be over-credited as resource-based. (2) TimeProvider is now injected into TokenService (iat/nbf/exp), the notification read handlers, and UserNotification.MarkAsRead(DateTime readOnUtc) (which now takes an explicit UTC timestamp, keeping that aggregate free of ambient clock): a genuine §14-testability/§1-DIP improvement. The one remaining domain DateTime.UtcNow: BaseDomainEvent.DateOccurred: was reviewed 2026-06-26 and accepted as a deliberate choice (occurrence-time = when the aggregate raises the event is the correct event-sourcing/audit semantic, enforced by four domain tests; relocating it to the persistence boundary regresses the semantic), and is now documented as such in BaseDomainEvent's <remarks> rather than treated as a defect. (3) ADR-019 (rate-limiting) now documents the authenticated-only global limiter: the limiter itself is pre-existing code (shipped 2026-06-12, commit 285a169), already scored under §11; v1.80.0 added only the ADR. Post-review hardening (2026-06-26): the limiter's load-bearing partition/exemption logic is now unit-tested (RateLimitPartitionTests), and the two notification handler tests now assert the stamped read-time against a fixed TimeProvider: closing the two test-coverage caveats above. Governance is mixed: ADRs 017/018 are now committed (a real fix), but ADR-019 + the README/CLAUDE index edits are uncommitted, the CHANGELOG still skips 1.72-1.78 and now also lacks a 1.80.0 entry (empty [Unreleased]), and the framework's own scorecard/backlog were not yet refreshed for this wave. (All since resolved in the v1.81.0/v1.82.0 governance pass: CHANGELOG completed, ADRs committed, and this scorecard migrated in-repo as the canonical two-axis evaluation; see the fifth wave.)
A fifth wave landed 2026-06-26 (v1.81.0→v1.82.0, HEAD 0dcac4d, clean tree at the v1.82.0 tag). Like the fourth, it lands in categories already scored 9-10, so no numeric score moves, but it converts three previously-anticipated or working-tree items into shipped, committed fact and clears the prior cycle's §34 governance nits: (1) the Scalar OpenAPI UI helper is now released (v1.81.0, not "committed-but-unreleased"): MapCommonScalarUi() renders /scalar/{doc} from the generated document, non-Production only, via the bundled Scalar.AspNetCore 2.16.6 (no CDN), so §9's interactive-UI gap is closed in a tagged release; (2) COST.md (FinOps notes, §31) is released (v1.81.0), documenting the framework's cost levers (telemetry-span filtering, outbox poll/retention tuning) and the right-sizing/attribution/surge-revert defaults consumers set; (3) RS256 is now pinned on the JWKS-forwarded auth path (v1.82.0, §11/§26): ValidAlgorithms = [RsaSha256] on the forwarded-JWT (JWKS-discovery) validation path in MMCA.Common.API, defense-in-depth against an algorithm-confusion swap, matching the pre-existing in-process pin; (4) ADRs 020-022 are added and committed: 020 (permission-based authorization, documenting the v1.80.0 layer), 021 (consumer-inbox idempotency, documenting the pre-existing EfInboxStore), 022 (browser session-cookie auth, documenting the SSR BFF token-storage scheme), bringing the tracked set to 001-022; (5) the §34 hygiene nits are resolved: ADR-019 is committed, the CHANGELOG is complete (1.69.0→1.82.0, no gaps), and the working tree is clean. Two test-hardening and one stability item also landed: the rate-limiter partition/exemption logic and the notification read-time clock are now unit-tested (§14), and the E2E register/login success-detection got a grace window to de-flake it (R11, §28). (The prior cycle's minor README.md:42 stale "001-016" parenthetical has since been corrected to 001-022.)
A sixth, governance-focused pass is in flight at this re-score (uncommitted working tree) and is the immediate basis for this re-verification. It moves no score (every change lands in a category already at 9-10) but adds durable governance substance: (1) FACTS.md is now generated from source by a dependency-free build/facts tool and gated in CI (ci.yml:27-28 runs dotnet run --project build/facts -- . --check), so the framework-wide facts (version, package count, ADR range, fitness-method/base counts) can no longer drift from source: a fitness function for the docs themselves (§34/§16). (2) The rubric (ArchitectureEvaluationCriteria.md) and FACTS.md are now version-controlled in this repo (previously workspace-only), completing the §34 governance pattern already applied to the ADRs and this scorecard. (3) Security-response headers were centralized into one pluggable middleware in MMCA.Common.Aspire.Security (AddCommonSecurityHeaders + ICspPolicyProvider + SecurityHeadersMiddleware, unit-tested), removing the per-host drift the previously hand-rolled headers allowed, and documented in the new ADR-023 (bringing the set to 001-023, §11/§26/§34). The one honest caveat: the tree is dirty and the CHANGELOG [Unreleased] does not yet carry ADR-023, so traceability is momentarily inconsistent until this pass is committed.
A seventh wave landed 2026-06-26/06-27 (v1.83.0→v1.84.0, HEAD b2b0aae), folded into this re-verification, and it moves one score. (1) The sixth-pass governance items are now committed and tagged: v1.83.0 (b9a6a28) shipped ADR-023 (security-response headers), the source-generated CI-drift-gated FACTS.md, and this two-axis scorecard, resolving the prior cycle's "ADR-023 uncommitted" §34 caveat. (2) PiiRedactor ships and moves §30 from Implementation 7 to 8 (v1.84.0, b2b0aae): Domain/Privacy/PiiRedactor.cs masks every [Pii]-marked member (shallow, value-erasing) before an entity carrying personal data reaches a structured log or telemetry attribute, with 7 PiiRedactorTests (incl. a "never emits the clear-text PII values" assertion). This closes the §30 red flag the rubric names verbatim ("PII in logs/telemetry"), which the prior cycle recorded as documented-but-missing; §30 maturity holds at 3 because DSAR/export endpoints, consent capture, the personal-data inventory, residency verification, and retention execution remain consumer-owned and PiiConventionTests still passes vacuously in-repo. (3) A warm-up / readiness subsystem ships (Aspire/Warmup/: WarmupHostedService, WarmupReadinessGate, WarmupReadinessHealthCheck, OpenIdConnectMetadataWarmupTask), wired into AddServiceDefaults so every host gates /health/ready on warm-up (gate starts closed, opens even on task failure = availability over warmth, with lazy-retry fallback under ADR-009; the built-in task pre-fetches OIDC discovery to kill ACA cold-start), now documented in ADR-025. It is genuine §29/§13 substance but was scored at v1.84.0 as enrichment, not a lift: it shipped with zero unit tests (since added in the eighth wave below, which lifts §29 Impl 7→8) and the §29-capping recovery gaps (no in-repo restore drill, RTO/RPO, or SLO/error-budget) are unchanged, so at v1.84.0 §29 held 3/7 and §13 holds 4/9. (4) ADR-024 (two-channel notifications) documents the pre-existing SignalR-push + durable UserNotification-inbox extension points (IPushNotificationSender/INotificationRecipientProvider with no-op defaults), enriching §6 evidence with no move. The honest caveat: at this re-score ADR-024/025 are untracked and the CHANGELOG [Unreleased] is empty while the repo is tagged through v1.84.0 (a transient §34 hygiene nit, tracked in the backlog).
An eighth wave (the under-8 Implementation remediation) shipped in v1.85.0 (commit 78e5312, tag v1.85.0) and is verified against source in this re-score. Every category whose Implementation sat below 8 was lifted with shipped, tested in-repo evidence rather than narrative: §5 (a SliceCohesionTestsBase fitness function enforcing slice co-location), §12 (a BenchmarkDotNet smoke harness + the framework page-size guard), §17 (a reference samples/deployment Bicep/OIDC sample + DEPLOYMENT.md), §24 (Register/Login converted to EditForm + DataAnnotations field-level validation), §25 (an in-shell Forbidden 403 page + NavigationFlow.md), §29 (warm-up subsystem unit tests + a RESILIENCE.md SLO/error-budget + restore-drill reference), and §31 (a built-in OTel Telemetry:TracesSampleRatio sampler knob + a per-message log trim). §5/§12/§17/§24/§25/§29 each reach Implementation 8 and §31 reaches 7 (full Release build clean, 1651 tests pass). The wave also moves one maturity score: §5 Vertical Slice 3→4, because its new slice-cohesion fitness function is automatic CI enforcement of the slice convention (the rubric's maturity-4 bar). A separate docs governance pass remains uncommitted at this writing (ADRs 024/025/026 untracked, the FACTS.md ADR-count bump, ADR cross-links, and a ServiceContractAttribute doc-comment correction that stops it claiming a non-existent [ServiceContract] arch test); ADR-026 documents the two-tier caching substrate (the ICacheService memory-or-distributed swap + the HTTP output-cache edge) that §10 already scored.
A ninth wave landed 2026-06-28/29 (v1.86.0→v1.92.0) and is the basis for this re-score, it moves five scores. The headline is internationalization: multi-locale i18n (en-US + Spanish) now ships in the framework itself (ADR-027, superseding the single-locale ADR-011): co-located .resx + IStringLocalizer<T>, backend errors localized at the edge keyed by the existing Error.Code, one culture cookie as the source of truth across SSR/Server/WASM (forwarded to services as Accept-Language), and a per-user PreferredCulture. This flips §27 from N/A to a scored Maturity 2 / Implementation 6 (immature: culture-less date/number formatting is guarded only by an advisory analyzer, with no missing-key/coverage CI gate), and removes the last N/A category, so all 34 categories now count toward the indices. Five further capabilities landed without moving a score, each enriching evidence: ADR-028 day/dark theme (the dark palette is now wired through MudThemeProvider with a ThemeService + a ThemeToggle in the shared layout, §20, where the raw-hex/!important deductions still hold); ADR-029 brute-force protection plus ADR-032 password hashing (PBKDF2-HMAC-SHA512, 32-byte salt, 600k iterations, FixedTimeEquals, with a load-bearing legacy-HMAC verify-and-migrate branch: §11, which still recalibrated to 8); ADR-030 startup sole-migrator (each service self-applies its EF migrations at boot: §8/§17, a runtime mechanism, not the CI migration-apply gate those categories' gaps name); and ADR-031 feature-flag management (Microsoft.FeatureManagement on both the controller [FeatureGate] and CQRS IFeatureGated surfaces: §10). The ADR set grew 026→032 and the source-generated FACTS.md advanced the fitness counts (71 methods across 18 bases, Common's own build executes 38). The honest caveat: ADR-032 is untracked and a docs/source pass (seven ADR edits, FACTS.md, one source edit) is uncommitted at this re-score.
The two axes remain deliberately asymmetric: at the ninth wave Maturity 91.7% vs Implementation 84.1% (current: 97.5% vs 86.3%, see the Indices section), keeping Implementation the weaker axis. Both indices dip slightly this cycle, and the dip is honest re-calibration rather than regressed work: §27 i18n enters both denominators at a low 2/6 (genuinely shipped, but newly so and only advisory-guarded), and §11/§22/§23 were trimmed to better-supported bands on a close re-read (algorithm-confusion defenses and a 3-engine matrix are real, but vault binding, a chromium-only gate, and unmeasured Web Vitals keep them out of the top band). The residual gap is structural rather than a quality defect: the framework consistently ships mature, well-governed mechanisms (ADRs, fitness functions, documented patterns) whose full execution necessarily completes in the consumer apps (ADC/Store). The in-repo half of §17 (a reference Bicep/OIDC sample) and §29 (warm-up tests + an SLO/restore reference) is done; the deeper §17 CD-to-Azure and §29 drilled-restore substance still lives downstream, and Data-migration gating (§8) remains maturity-above-implementation because a verifiable CI migration-apply step lives in the consumer repos (ADR-030's boot-time self-migration is a runtime mechanism, not that gate). Compliance (§30) and FinOps (§31) are the inverse: their in-repo implementation (the PiiRedactor log-masking + IAnonymizable erasure extension point, now gated by PiiErasureContractFitnessTests; the documented cost-lever defaults) leads a maturity that stays capped because the governing process (DSAR/consent capture, the personal-data inventory, residency verification, per-service cost attribution, right-sizing) is consumer-resident.
The practical implication: closing the remaining Implementation gap is mostly about adding evidence and verification of execution inside MMCA.Common itself (the committed OpenAPI baseline-drift check this named for §9 shipped 2026-08-22 in PR #271), an in-repo restore drill + baseline SLOs (§29), a CI migration-apply step against an ephemeral DB (§8), and a smoke-deploy of the reference Bicep/OIDC sample (§17): rather than re-architecting. The freshest gap this cycle is the newly-scored §27 i18n: lifting it toward maturity 4 needs a missing-key/translation-coverage CI gate and a documented locale-addition governance step. There are no longer any N/A categories: §27 Internationalization, previously excluded under the single-locale ADR-011, is now an active scored category after ADR-027 shipped en-US + Spanish in the framework.
A twenty-first-wave re-score ran 2026-07-21 at v1.121.0 (HEAD 4a4fc05, clean tree) and moves exactly one score. §12 Performance & Scalability rises Maturity 3→4. The nineteenth wave dropped it to 3 for lack of a real regression gate, the twentieth wave kept it at 3 on the narrower ground that the shipped gate's job context was absent from live branch protection. That ground no longer holds: main now requires 8 status checks including Performance gate (BenchmarkDotNet Short + baseline verify), and the job carries no continue-on-error (.github/workflows/ci.yml:179,196-204), so a latency or allocation regression against the committed baseline blocks the merge. That is automatic enforcement, the rubric's Maturity-4 bar. Implementation holds at 8 because load and stress timing against realistic volumes still lives in the consumer apps. The cycle's more useful finding is what did not move: first-pass scoring proposed lifts in seven categories (§4, §9, §11, §19, §20, §26, §29) and an adversarial second pass refuted every one against source, each for a concrete reason rather than caution: only Notifications is a bounded context in-repo (§4), the three contract-test bases are subclassed downstream and never in Common's own tests (§9), the sample's Key Vault secret binding is still dangling in the bicep template (§11), and no commit has touched resilience, restore-drill, or warm-up files since the twentieth-wave baseline (§29). One documentation drift was surfaced and routed to the backlog under #34 rather than scored: CONTRIBUTING.md still advertises five required gates and calls webkit advisory, while live protection requires eight including webkit, the Helpdesk consumer-source canary, and the perf gate. Because scorecard adjudications cite that file, its drift can mis-ground a future score, which is precisely what happened to §12 this cycle.
A twenty-second-wave re-score ran 2026-07-23 at v1.123.0 (HEAD c911480, clean tree) and moves nothing: all 34 categories re-confirm at their twenty-first-wave values. The cycle's value is again in what it refused to move. Three first-pass lifts were refuted against source: §9 (Maturity 3→4) because OpenApiContractTestsBase is still subclassed only in consumer hosts, never in Common's own tests, and OpenApiEndpointExtensions.cs:13 records that delegation as deliberate, so no in-repo CI gate enforces the contract; §10 (Implementation 8→9) because the three documented hold-reasons are still in code (the distributed cache path is a no-op without a real IConnectionMultiplexer, DependencyInjection.cs:158; the idempotency semaphore is in-memory, not cross-instance-exclusive; resilience config is partly literal); and §30 (Maturity 3→4) because the only automatic gate, PiiErasureContractFitnessTests, proves the erasure mechanism while the structural [Pii] convention scan stays vacuous in-repo and the governing process (inventory, DSAR, consent, residency, retention) is consumer-resident. Two first-pass downgrades were also refuted: §23 and §24 (each Implementation 8→7) failed on a fresh re-read of every cited file: no regression exists, and §23's web-vitals budget gate is now enforced on all three browser engines (ci.yml:107), stronger than when its 8 was set. The v1.122.0-v1.123.0 train (the typed filter DSL operators, the EntityQueryPipeline page-size clamp, cache-observability warnings, the IIntegrationEventPublisher removal with callers moved to IEventBus) lands as evidence enrichment with no band move. Indices hold at Maturity 96.9% (314/324) / Implementation 84.6% (685/810).
A twenty-third-wave re-score ran 2026-07-25 at v1.128.0 (HEAD 3dff29b, clean tree) and again moves nothing: all 34 categories re-confirm, the third consecutive steady-state cycle at 96.9% / 84.6%. Five first-pass proposals were refuted on the adversarial pass, and two of the refutations sharpen a standing row rather than merely declining it. §17 DevOps (proposed 3/8 → 4/9, held): the maturity-3 hold-reason is unchanged in source (ci.yml:591-592 still records that a real what-if/deploy stays a consumer-side concern), and the verification surfaced that the sample-deployment-validate context is absent from the 8 required status checks on main, so the row's own "blocking" claim was drifted; the row is corrected here to say compile-check-on-pull-request, not merge gate. The genuinely new §17 evidence is real but partial: releases now publish to nuget.org as well as GitHub Packages using NuGet/login OIDC trusted publishing with no stored API key (release.yml:79-88,155-165, ADR-053), which strengthens the secretless-deploy-identity criterion without touching either hold-reason, so it lands as evidence enrichment. §31 FinOps (proposed Maturity 2→3, held): a search for cost, FinOps, and budget across .github and Tests/Architecture returns nothing, so no cost convention is enforced by review or CI, and every cited artifact is byte-identical to the v1.123.0 tree already scored at 2. §30 holds at Maturity 3 (the structural [Pii] scan is still vacuous in-repo), §34 holds at Implementation 8 (Docs/Architecture/ArchitecturalAnalysis.md still lives at the uncommittable workspace root, with no committable replacement anywhere under docs-src/ or MMCA.Common), and the one proposed §23 downgrade fails on a fresh read: WebVitalsE2ETests asserts its LCP/TTFB/CLS budgets inside the unfiltered ui-e2e job with no continue-on-error anywhere. The cycle's real output is documentation hygiene, routed to the backlog under #34: this scorecard's own prose had drifted five minor versions behind the CI-gated FACTS.md (§14's fitness counts read 85/28/49 against an actual 93/30/56, and §16/§34 read ADRs 001-051 against an actual 001-055), all corrected in this refresh.
A twenty-fourth-wave re-score ran 2026-07-28 at v1.131.0 (HEAD 2c52aa9, clean tree) and again moves nothing: all 34 categories re-confirm, the fourth consecutive cycle at 96.9% / 84.6%. Five first-pass proposals were refuted on the adversarial pass, and the shape of the refusals is now the stable signal: every one of them is a criterion the framework cannot close from inside a library. §9 API & Contract (proposed Maturity 3→4, held): the rubric's Maturity-4 bar is automatic enforcement, and neither §9 enforcement mechanism exists in-repo. The OpenAPI contract-drift gate is delegated to consumer integration tiers by the framework's own comment (OpenApiEndpointExtensions.cs:12-14), the shipped OpenApiContractTestsBase has no MMCA.Common subclass, and the [ServiceContract] architecture rule is still absent by its own docstring (ServiceContractAttribute.cs:6-10); the only CI-gated §9 tests are two DI-registration facts for AddCommonApiVersioning (WebApplicationBuilderExtensionsTests.cs:17,29) that assert registration presence, not a contract property. §13 Observability (proposed Implementation 8→9, held): the two criteria that set the twentieth-wave hold (SLO alerting/dashboards, runbooks) are still unmet in-repo, which is the Strong band, not Exemplary. §25 Navigation & IA (proposed 8→9, held): no §25 source changed since the tree scored at 8, and the "parameters typed and validated" half of the route-design criterion is still structurally unexercised. §29 Resilience (proposed 8→9, held): the framework's own guide records restores, RTO/RPO and SLO alerting as executed in the consumer apps' IaC (common-RESILIENCE.md:3,27), and the only new §29 work (health-check tagging plus the readiness-gate fix) is opt-in enrichment of a criterion already partly met. §30 Compliance & Privacy (proposed Maturity 3→4, held for the third consecutive cycle): zero files matching pii/privacy/retention/anonym/consent/erasure/encrypt changed between the twenty-third-wave baseline (3dff29b) and this HEAD, so the proposal re-cited the identical evidence that already grounds Maturity 3. The cycle's real output is again documentation hygiene, routed to the backlog under #34: this scorecard's prose had drifted three minor versions behind the CI-gated FACTS.md (§14's fitness counts read 93/30/56 against an actual 96/31/61, and §16/§34 read ADRs 001-055 against an actual 001-060), and six evidence anchors had drifted with the code (§9's AddCommonOpenApi and header-versioning anchors, §13's AddWarmupReadiness call site, and §17's sample-deployment-validate comment and step lines), all corrected in this refresh. One deliberate choice was recorded rather than scored: the consumers skipped v1.128.0 through v1.130.0 and swept 1.127.0 straight to 1.131.0 in one pass (CHANGELOG.md:9-15), which is ADR-016 lockstep behavior, not three missed sweeps.
A twenty-fifth-wave re-score ran 2026-08-01 at v1.135.0 (HEAD f292233, clean tree) and moves one score, ending the four-cycle steady state: §10 Cross-Cutting Concerns Implementation 8→9. The move rests on new shipped evidence, not recalibration: the idempotency guard's cross-instance-exclusivity hold-reason (recorded verbatim since the twenty-second wave) is closed in source. IdempotencyFilter now resolves an IDistributedLock and falls back to the striped per-process semaphore only when a host registers none (IdempotencyFilter.cs:31-34,109,161-164), and AddCaching registers RedisDistributedLock (SET NX PX acquire + compare-and-delete Lua release, real cross-replica mutual exclusion, Infrastructure/DependencyInjection.cs:181-195, Concurrency/RedisDistributedLock.cs:36) whenever an IConnectionMultiplexer is present; the filter's distributed-lock path is unit-covered and ADR-017 is revised to document the guard. Held at 9, not 10: prefix eviction stays TTL-only with no multiplexer, HttpResilienceDefaults values are compile-time constants, and the Redis lock is mock-tested only. Four first-pass proposals were adversarially refuted and held at prior. §2 Design Patterns (I9→10): the four cited files are byte-identical to the baseline that scored 9, and a rubric-named §2 red flag is live in shipped code (ServiceExceptionHelper.ThrowIfDomainExceptionAsync re-throws API error payloads as DomainInvariantViolationException from three shipped UI service bases, and pages type-sniff that exception to pick the user-facing message: documented and deliberate under ADR-027's rationale, but exceptions-as-control-flow where Result<T> is the framework's own convention is not trivial polish). §15 Best Practices (I9→10): the NoWarn surface grew rather than shrank (S8970 added 2026-07-28) and three §15-tagged backlog items are open, two re-verified live in source (CD-1's silently dropped where/orderBy on GetAllForLookupAsync, CD-2's untranslatable lookup ToString()); the one hold-reason that did close (the dated NuGetAuditSuppress, removed 2026-07-20, accepted-advisory list now empty) supports 9, not 10. §17 DevOps (3/8→4/9): no §17 source changed since the v1.128.0 tree already scored 3/8, the Bicep job is compile-only and absent from the 8 required contexts (verified live via the branch-protection API), and the verification surfaced a new defect in the reference IaC: samples/deployment/main.bicep:143 binds secretRef: 'sql-conn' but the template declares no secrets array anywhere, so the sample would be rejected at deploy time and az bicep build does not catch it (routed to the backlog under #17). §31 FinOps (I7→8): refuted a third time on byte-identical evidence (the newest §31 artifact remains the v1.118.0 metric-family knob pair); the documented acceptance cap stands. A scoring recalibration also lands this cycle (2026-08-01): Implementation 10 is awardable for an almost perfect implementation (every criterion at reference quality, no red flags, at most trivial polish left), so the "attainable ceiling" reporting line is retired and the implementation index reads directly against 100%; the denominators stay ×4/×10, so the trend line is unbroken, and the backlog's scheduling target stays 9 (the 9→10 rung is recognition at re-score time, not scheduled work). Doc hygiene corrected in this refresh: the §1/§2 Result.cs anchors had drifted (Match/Map/BindAsync now at :165/:181/:194), §2's decorator-registration anchor is :88-102, §15's row cited the removed NuGetAuditSuppress and a stale analyzer-ItemGroup anchor (now Directory.Build.props:63-84), and the §16/§34 ADR range read 001-060 against an actual 001-064 (ADR-061 runtime secret management through ADR-064 deploy recency gates, added in the 2026-08-01 ADR audit). The FR-1..FR-7 deferral ledger was not individually re-verified this cycle and stays open as recorded. Both consumers are pinned to 1.135.0 (MMCA.ADC/Directory.Packages.props:127, MMCA.Store/Directory.Packages.props:8). Net indices: Maturity 96.9% (314/324) unchanged, Implementation 84.6% → 84.8% (685 → 687/810, +2 from §10 on weight 2). Implementation stays the weaker axis (gap ~12.1 points); no category is N/A. The Indices section below is authoritative.
A twenty-sixth-wave re-score ran 2026-08-07 at v1.142.0 (HEAD 710d29d, clean tree) and moves nothing: all 34 categories re-confirm at their twenty-fifth-wave values. Seven first-pass lift proposals were adversarially refuted and held at prior, and every refutation rests on evidence read this run rather than caution. §6 CQRS (proposed Implementation 9→10, held): event-schema versioning remains a signal with no mechanism (BaseIntegrationEvent.SchemaVersion defaults to 1 and ADR-010's own trade-off section records the upcaster registration extension point as unbuilt follow-up work; a repo-wide search for upcasters returns only the CHANGELOG and a doc comment), the framework's own event-versioning fitness gate stays vacuous in-repo (Common ships no concrete integration event), and consumer-side idempotency is opt-in and off by default (MessageBusSettings.EnableInbox defaults false, DI registers NoOpInboxStore unless a host opts in); the v1.135.0..HEAD range touches only CachingCommandDecorator, the EfInboxStore duplicate-detach fix, and outbox metrics, none of which closes a hold-reason. §11 Security and §12 Performance (each Implementation 8→9, held): both categories' stated caps are still open in current source and no criterion-closing §11/§12 evidence has landed since the 2026-08-01 re-score. §17 DevOps (proposed 3/8→4/9, refuted for the third consecutive cycle on byte-identical evidence): the Bicep validate job stays compile-only and absent from the required contexts, and the reference template's dangling secretRef: 'sql-conn' binding (backlog #17) is re-verified live at samples/deployment/main.bicep:143. §25 Navigation & IA (proposed Implementation 8→9, held): all ten framework routes are still parameterless string templates (NavigationFlow.md:13-21, zero route parameters or constraints in any MMCA.Common.UI page), the only typed-ish query state remains the sanitized ?returnUrl= (Login.razor:127), OAuthComplete's query parameters are untyped strings, and DeepLinkListener navigates its route strings unsanitized (DeepLinkListener.razor:26,36), so the hold-at-8 reason is literally true in source; the Maturity-4 gate is independently re-confirmed (NavigationContractTests.cs:29,44, in the CI-gated unit tier at MMCA.Common.slnx:46, an anchor drift corrected this refresh). §26 Front-End Security (proposed Implementation 8→9, held): the default static CSP still omits script-src/style-src (Aspire/Security/SecurityHeaders.cs:42-48); no §26 code has landed since 2026-07-05, and the likely new-evidence candidate (BlazorCspPolicyProvider, which does carry both directives) predates both prior refutations of this same lift, so proposing 9 re-litigates the eighteenth- and twenty-first-wave adjudications on byte-identical evidence. §31 FinOps (proposed Maturity 2→3 / Implementation 7→8, held): no §31 artifact has landed since v1.118.0 (2026-07-17); the documented acceptance cap stands (fourth consecutive cycle it survives a proposed lift). The cycle's real output is again documentation hygiene under #34: the governance prose had drifted behind the CI-gated FACTS.md (fitness counts read 96/31/61 against an actual 100 methods across 32 bases, Common runs 78, per FACTS.md:44-48; the §16/§34 ADR range read 001-064 against an actual 001-070), one §25 evidence anchor had drifted (MMCA.Common.slnx:45→:46), and FACTS.md no longer states the ADR range itself: it delegates the count/range to the Website ADR index (FACTS.md:38-41), so ADR-range citations now point at docs-src/adr/README.md. All corrected in this refresh. One live consumer-state note: the framework is at v1.142.0 with MMCA.Store already pinned to 1.142.0 while MMCA.ADC pins 1.141.0 with its bump open as ADC PR #106, an in-flight sweep, not drift. Three stale backlog entries whose categories already score M4/I9 (#18, #20, #32) are closed to the protect list in the companion RemediationBacklog.md reconciliation; no index effect. Net indices: Maturity 96.9% (314/324) and Implementation 84.8% (687/810), both unchanged. Implementation stays the weaker axis (gap ~12.1 points); no category is N/A. The Indices section below is authoritative.
A twenty-seventh-wave re-score ran 2026-08-14 at v1.152.0 (HEAD 3ba8d13, clean tree) and moves nothing: all 34 categories re-confirm at their twenty-sixth-wave values. Nine first-pass lift proposals were adversarially refuted and held at prior, and every refutation rests on evidence read this run. §5 Vertical Slice (proposed Implementation 8→9, held): the one genuinely new §5 fact, the Application/Users/UseCases/{ChangePassword,ChangePreferences,DeleteUser,ExportUserData,GetPreferences} family (landed aa73eff/ad509d3), is real but unenforced: every one of those five handlers is abstract while the slice-cohesion gate scans only concrete classes (RuleHelpers.cs:36), four of the five ship no framework-owned contract at all (the command record is deliberately app-side, ChangePasswordHandlerBase.cs:17-29), and every Users use case emits through the shared UserUseCaseLog switchboard, so the enforced in-repo slicing surface is still the Notifications family plus one validator; the §5 row's prose is refreshed to record the new family honestly. §11 Security, §13 Observability, §25 Navigation & IA, §29 Resilience, §34 Governance (each Implementation 8→9, held): each category's recorded hold-reasons are re-verified live in current source with no criterion-closing evidence since the prior baseline (§25's nine framework routes remain parameterless literal templates; §13's SLO alerting/dashboards and runbooks stay deployer-owned; §29's real backup/restore of a production store and measured SLOs remain in-repo templates; §34's durable cap, the uncommittable ArchitecturalAnalysis.md, is unchanged). §26 Front-End Security (proposed Implementation 8→9, refuted a fourth time on byte-identical evidence): the default static CSP still omits script-src/style-src, and the only §26-adjacent commits since 2026-08-06 are packages.lock.json bumps. §17 DevOps (proposed 3/8→4/9, refuted a fourth consecutive cycle): the only §17 diff since the prior score is five documentation lines in samples/deployment/DEPLOYMENT.md; foundation.bicep, main.bicep, ci.yml and release.yml are byte-identical to the already-adjudicated state, and the dangling secretRef: 'sql-conn' defect is re-verified live at main.bicep:143. §30 Compliance & Privacy (proposed Maturity 3→4, held for the fifth consecutive cycle, on genuinely new evidence this time): real §30 substance shipped 2026-08-13/14 (a DSAR export controller base behind the Privacy.DataExport feature flag, ADR-076; a field-level audit trail that records the redaction token for [Pii] members on both sides of every change, with a 90-day retention purge job, ADR-075), which weakens the old "DSAR endpoints and retention execution are consumer-resident" leg of the M3 rationale, but the new surfaces are opt-in twice over (AuditTrailSettings.Enabled defaults false, the purge runs only if the host also runs the scheduler) and no new automatic category-level gate landed: the only two §30 gates remain the unchanged vacuous convention scan and the single-sample mechanism test, so review-enforced Maturity 3 stands and Implementation 8 is confirmed (consent/lawful basis absent, no PII-inventory artifact, and the CSV export row-scoping hook fail-open by default, EntityControllerBase.cs:488). The cycle's doc hygiene, corrected in this refresh: the §16/§34 ADR range read 001-070 against an actual 001-078 (ADR-071 barcode scanning + QR display through ADR-078 CSV export endpoint, including the 2026-08-14 enterprise wave's ADRs 073-078), and the §5/§30 row prose had drifted behind the new source as described above. One live consumer-state note: both consumers are re-converged on the framework at v1.152.0 (MMCA.Store/Directory.Packages.props:9-10, MMCA.ADC/Directory.Packages.props:96); the twenty-sixth wave's in-flight ADC sweep is closed and lockstep is intact. Net indices: Maturity 96.9% (314/324) and Implementation 84.8% (687/810), both unchanged. Implementation stays the weaker axis (gap ~12.1 points); no category is N/A. The Indices section below is authoritative.
A thirty-second-wave re-score ran 2026-09-04 at v1.185.0 (HEAD 7018a48, clean tree) and moves two scores, both on the Implementation axis and in opposite directions. (The twenty-eighth through thirty-first waves are recorded on the header line above, in the Indices block, and in git history.) §31 Cost Efficiency / FinOps rises Implementation 7→8, the first §31 move since the twelfth wave, on new in-tree substance rather than recalibration: the default-ON Telemetry:FilterProbeTelemetry knob wires ProbeTelemetryFilter into both the ASP.NET Core and HttpClient tracing instrumentation (Aspire/Extensions.cs:224), and a ProbeTelemetryFilterProcessor registered ahead of the exporters un-records the probe-child dependency spans the inbound filter cannot reach, the health check's SQL SELECT 1 and the Redis PING (Extensions.cs:248, Aspire/Telemetry/ProbeTelemetryFilterProcessor.cs:20), with 27 CI-gated tests behind it (MMCA.Common.slnx:46). It shipped in v1.182.0, the release the CHANGELOG titles "Cost release" (CHANGELOG.md:136), after v1.181.0 made the Telemetry:DisableHttpClientMetrics and DisableRuntimeMetrics toggles actually hold under UseAzureMonitor() by registering MetricStreamConfiguration.Drop views (CHANGELOG.md:187), so the "byte-identical evidence since the v1.118.0 knob pair" ground of the four prior refutations no longer holds. The row stops at 8 and Maturity stays 2 for the reasons the acceptance always named: right-sizing and reversible scale-events are consumer-side actions the framework only recommends (common-COST.md:69), cost attribution and budgets exist only as compile-only sample IaC (samples/deployment/main.bicep:166), and Tests/Architecture still returns zero cost/budget/FinOps matches, so no §31 convention is machine-enforced. §7 Microservices Readiness falls Implementation 9→8, and the trigger is a rubric change, not a re-litigation of the band recalibration eight prior cycles declined: rubric v2 added "Modernization patterns named: an Anti-Corruption Layer wraps any legacy or third-party model at the boundary, and extracting a module ... follows the Strangler Fig route" (ArchitectureEvaluationCriteria.md:263, added by ADR-110:55), and neither term appears anywhere in MMCA.Common source, docs, or any ADR. The pattern is practised without being named: the gRPC extension point tells hosts to "register a hand-written adapter that implements the C# interface contract ... and delegates to this typed gRPC client" (Grpc/DependencyInjection.cs:58), which is an Anti-Corruption Layer by construction, while ADR-008 documents a cutover ("Delete the combined MMCA.ADC.WebAPI host", 008-service-extraction-topology.md:33) rather than the new-path-beside-old, traffic-moved, old-path-retired sequence the criterion asks for. One wholly unmet criterion against seven met at high quality is the top of the rubric's Strong band, so the row lands at 8, not lower, and Maturity 4 is untouched: the extraction boundary is enforced automatically, not by review (Rules/Layering/ArchitectureRules.Transport.cs:19 subclassed through Bases/Layering/MicroserviceExtractionTestsBase.cs:13 and MicroserviceExtractionTests.cs:11, in MMCA.Common.slnx:52, run at ci.yml:144 under a required check). The path back to 9 is documentation, not code: name the ACL over the gRPC adapter convention and record the Strangler Fig route in ADR-007/008. §10 Messaging & Integration Architecture is scored fresh under its v2 name for the first time and comes back CONFIRMED at M4/I9, retiring the rubric-v2 rebase caveat that carried it at prior scores: broker topology by ADR, BrokerEventBus writing through the outbox with BrokerMessageBus on RabbitMQ or Azure Service Bus, exponential UseMessageRetry layered over second-level delayed redelivery (Infrastructure/DependencyInjection.cs:961,957), a fault consumer plus dead-letter retention and ReplayDeadLettersAsync, an EF inbox staged in the handler's own transaction, ADR-090 upcasting for contract evolution with fitness-enforced versioning, and ADR-086's documented saga deferral with the ADR-054 choreography alternative as the accepted interim. Four first-pass lifts came back FLAG and are held at prior by merged-prior policy, each on evidence read this run: §12 8→9 (load/stress and evidence-based capacity stay consumer work, backpressure is broker defaults), §13 8→9 (main.bicep:155 comments "SLO alerting" while :156 declares only an action group, no metricAlerts resource), §17 3/8→4/9 (the Bicep validate job is compile-only and not a required context; main.bicep:143 still binds secretRef: 'sql-conn' with no secrets: array), and §30 3/8→4/9 (no §30 substance between v1.179.0 and HEAD, the eighth consecutive refusal). The indices land at Maturity 97.5% (312/320, unchanged) and Implementation 86.3% (690/800, from 86.4%), so Implementation stays the weaker axis, now by 11.2 points.
Scorecard
| # | Category | Weight | Maturity (0-4) | Impl (0-10) | Weighted (M·w / I·w) | Evidence / Notes |
|---|---|---|---|---|---|---|
| 1 | SOLID Principles | 3 | 4 | 9 | 12/27 | All five principles met and machine-enforced: DIP via Application-owned ports + NetArchTest/MSBuild guards (reinforced in v1.80.0 by TimeProvider injection replacing ambient DateTime.UtcNow in TokenService/notification handlers), OCP via decorator pipeline + the new permission registry as a single change-point for authz grants (no growing switch), ISP via narrow marker interfaces, SRP via single-method handlers/decorators, LSP clean. Held at 9 by a few broader base-controller/UoW surfaces. Evidence: Application/Interfaces/Infrastructure/IUnitOfWork.cs; Application/DependencyInjection.cs:94-103 (Scrutor TryDecorate, innermost-first → exec order FeatureGate→Logging→Caching→Validating→Transactional→Handler); Shared/Abstractions/Result.cs:165-194 (Match/Map/BindAsync) |
| 2 | Design Patterns | 2 | 4 | 9 | 8/18 | Idiomatic, problem-driven: Result over exceptions, Repository/UoW, Specification with composition (now incl. InlineSpecification + the polyglot CrossSourceSpecification), Decorator pipeline, entity Factory+Result, Outbox, Strategy: consistently named; no pattern-theater or anemic manager/util classes; vetted libs (Scrutor/MassTransit/FluentValidation). Evidence: Shared/Abstractions/Result.cs:165/181/194 (Match/Map/BindAsync); Domain/Specifications/Specification.cs:62/88/114 (And/Or/Not expression-tree composition, EF-translatable) + :45 (InlineSpecification); Application/Specifications/CrossSourceSpecification.cs:39; Application/DependencyInjection.cs:88-102 |
| 3 | Clean Architecture | 3 | 4 | 9 | 12/27 | Dependency rule points strictly inward, enforced twice (MSBuild targets + NetArchTest); domain verified framework-free; Application defines ports, Infrastructure implements adapters. No circular refs / shared-kernel dump. Evidence: Source/Build/MMCA.Common.LayerEnforcement.targets:1-90 (compile-time guard, BeforeTargets ResolveProjectReferences, imported Directory.Build.props:87-88); Tests/Architecture/.../LayerDependencyTests.cs:12-58; .../DomainPurityTests.cs:13-26 (Domain forbidden EF/ASP.NET/Serilog/AutoMapper/Newtonsoft/+more) |
| 4 | Domain-Driven Design | 3 | 4 | 8 | 12/24 | Strong tactical DDD: aggregates with enforced invariants + domain events, immutable VOs with Result factories, identifier aliases vs primitive obsession, and an AggregateConventionTests fitness fn pinning Create→Result<T>. v1.80.0 nudges domain purity: UserNotification.MarkAsRead(DateTime readOnUtc) now takes an explicit UTC timestamp instead of reading an ambient clock, with TimeProvider injected at the handler edge. Held at 8 (not lifted): strategic-DDD (bounded contexts/ubiquitous language) is still realized downstream (only Notifications lives here). The one remaining domain DateTime.UtcNow: BaseDomainEvent.DateOccurred, is a deliberate, documented choice, not a defect (reviewed 2026-06-26): a domain event's occurrence instant is the moment the aggregate raises it, which is the correct event-sourcing/audit semantic (four domain tests enforce it); moving the stamp to the persistence boundary would regress occurrence-time → persist-time, and threading a clock through every aggregate is disproportionate. Evidence: Domain/Entities/AuditableAggregateRootEntity.cs:13-105; Domain/Notifications/.../UserNotification.cs:53-58 (explicit-timestamp injection point); Domain/DomainEvents/BaseDomainEvent.cs (deliberate occurrence-time default, see <remarks>); Shared/ValueObjects/{Money,Address,Email,PhoneNumber,DateRange}.cs; Tests/Architecture/.../AggregateConventionTests.cs:24-48 |
| 5 | Vertical Slice Architecture | 2 | 4 | 8 | 8/16 | Slice cohesion is now machine-enforced: a SliceCohesionTestsBase fitness function (shared MMCA.Common.Testing.Architecture) fails the build if a handler/validator is stranded from its same-assembly command/query contract, automatic CI enforcement of the slice convention, which meets the rubric's maturity-4 "enforced automatically by tests/CI" bar (matching every other fitness-gated category here). Where features exist they are clean vertical slices (Feature/UseCases/{Action}/ co-locating command/query + handler + validator + DTO) with cross-cutting in the pipeline. Impl held at 8 (not 9) because the framework's own primitives are deliberately horizontal (correct for infra) and the enforced in-repo slicing surface is still the Notifications family: a second sliced family now exists (Application/Users/UseCases/{ChangePassword,ChangePreferences,DeleteUser,ExportUserData,GetPreferences}, 2026-08-03/14), but its five handlers are all abstract bases outside the concrete-class slice-gate scan (RuleHelpers.cs:36), four of the five keep their command contract deliberately app-side (ChangePasswordHandlerBase.cs:17-29), and each Users use case emits through the shared UserUseCaseLog switchboard, so it deepens the pattern without widening the enforced surface. Evidence: Source/Hosting/MMCA.Common.Testing.Architecture/ArchitectureRules.Slices.cs + Bases/SliceCohesionTestsBase.cs; Tests/Architecture/.../SliceCohesionTests.cs; Application/Notifications/PushNotifications/UseCases/Send/; .../UserNotifications/UseCases/{MarkRead,MarkAllRead,GetInbox,GetUnreadCount}/; Application/Users/UseCases/ (abstract handler-base slices, unenforced by the gate) |
| 6 | CQRS & Event-Driven | 2 | 4 | 9 | 8/18 | Real command/query separation with an enforced, documented decorator pipeline; atomic outbox persist + dual dispatch; genuinely idempotent inbox consumer (dedup by MessageId via IInboxStore, now formally documented in ADR-021 as the broker-consume sibling of ADR-003's outbox + ADR-017's HTTP-edge idempotency). Event-schema versioning present: BaseIntegrationEvent.SchemaVersion => 1 (ADR-010) + an EventVersioningConventionTests fitness gate, removing the prior unversioned-contract risk. Held below 10 only because SchemaVersion is a default with no upcaster/compat policy yet demonstrated and read models are DTO projections. Evidence: Domain/DomainEvents/BaseIntegrationEvent.cs:22; Infrastructure/Persistence/Outbox/OutboxProcessor.cs (drain :221, dead-letter :282-283, retry :305-308); Persistence/Inbox/EfInboxStore.cs:29 |
| 7 | Microservices Readiness | 3 | 4 | 8 | 12/24 | Bounded-context modules extractable without rewrites (ADR-008), real data ownership per service (ADR-006), async events + gRPC only where sync needed, resilience policies, versioned contracts, trace propagation. Extraction boundaries enforced by fitness functions, not convention. No distributed-monolith/shared-table flags. ↓ Implementation 9→8 (thirty-second wave, 2026-09-04, rubric-change deduction, no work lost): rubric v2 added the criterion "Modernization patterns named: an Anti-Corruption Layer wraps any legacy or third-party model at the boundary, and extracting a module ... follows the Strangler Fig route" (ArchitectureEvaluationCriteria.md:263, added by ADR-110:55), and neither term appears in MMCA.Common source, docs, or any ADR. The ACL ships unnamed as an enforced convention ("register a hand-written adapter that implements the C# interface contract ... and delegates to this typed gRPC client", Grpc/DependencyInjection.cs:58), and ADR-008 documents a cutover ("Delete the combined MMCA.ADC.WebAPI host", 008-service-extraction-topology.md:33), not the new-path-beside-old, traffic-moved, old-path-retired sequence the criterion asks for. The other seven criteria are met at high quality, so the row sits at the top of the Strong band; naming the ACL over the gRPC adapter convention and recording the Strangler Fig route in ADR-007/008 returns it to 9. Maturity holds at 4: the boundary is a required-check merge gate, not review enforcement. Evidence: ADR-006; Application/Messaging/IMessageBus.cs:28 (InProcess vs Broker impls); Testing.Architecture/Rules/Layering/ArchitectureRules.Transport.cs:19 (TransportDoesNotLeakIntoCoreLayers, rule body in the shared package) + Bases/Layering/MicroserviceExtractionTestsBase.cs:13, subclassed at Tests/Architecture/.../Layering/MicroserviceExtractionTests.cs:11 (in MMCA.Common.slnx:52, run at ci.yml:144); Domain/Attributes/EventNameAttribute.cs:22 (versioned contract identity) + Infrastructure/Messaging/Consumers/UpcastingIntegrationEventConsumer.cs:32 (ADR-090 upcasting); Infrastructure/Messaging/Consumers/IntegrationEventConsumer.cs:54 (inbox row staged in the handler's own unit of work; path and line corrected 2026-09-04 from Infrastructure/Services/...:42); Grpc/DependencyInjection.cs:106 (attempt + total timeout, retry budget, circuit breaker); Application/Modules/ModuleLoader.cs:264 (Kahn ordering) and :108 (stub registrations keep cross-module interfaces resolvable in an extracted single-module host) |
| 8 | Data Architecture | 3 | 4 | 9 | 12/27 | Audit fields stamped centrally via AuditSaveChangesInterceptor (not per-handler), soft-delete + global filters via reflection over IAuditableEntity, optimistic concurrency tokens on every auditable entity, batch-by-IDs eager-loading vs N+1, per-service migrations/isolation. Now broadened by shipped polyglot persistence (ADR-018): three storage engines behind one entity model, engine chosen per-entity by [UseDataSource] on an engine shim base, cross-engine relationships auto-degraded (CrossDataSourceDegradeConvention), an engine-portable CrossSourceSpecification.BuildAsync, and a SpecificationsDoNotNavigateToOtherEntities build gate, proven by unit/integration tests. ↑ Implementation 8→9 (thirtieth wave, 2026-08-31, commit 59d7a97/PR #325): the long-standing hold reason, no verifiable CI migration-apply step, is closed in-repo: the required-gate Consumer source build (Helpdesk) CI job now applies a real consumer's committed EF migrations to an ephemeral SQL Server via dotnet ef database update built against the PR's framework source, with the outcome asserted rather than exit-code-trusted (__EFMigrationsHistory row count plus Tickets.Ticket and the framework-owned dbo.OutboxMessages must exist; ci.yml:578,597, the job sits in the required contexts and the workflow carries zero continue-on-error), plus an in-repo DB-free apply proof in the CI-gated unit tier (MigrationApplyProofTests.cs:92: a committed migration applied through DbContextFactory.MigrateAsync, history row + created table asserted; both the Infrastructure test project and its MigrationsFixture project are in MMCA.Common.slnx:36) and a production schema-drift guard (the None strategy validates no pending migrations on any source and throws if behind, DatabaseInitializationExtensions.cs:92). The FR-5 cascade gap is machine-gated too: an IL-level cascade-soft-delete fitness rule (an aggregate root owning a child collection must cascade in its Delete() override, ArchitectureRules.CascadeSoftDelete.cs:35) plus a hard-delete ban outside four individually named, reviewed framework types (SoftDeleteEnforcementTests.cs:19). Held at 9 (not 10): in-repo the cascade rule is a ratchet (Common's Source declares no child-bearing aggregate, CascadeSoftDeleteConventionTests.cs:8-12) and the default Delete() still flips IsDeleted on the single entity only (AuditableBaseEntity.cs:67); cross-source consistency stays best-effort sequential (no 2PC, ADR-006), now with a commit-phase failure surfaced as an explicit ambiguity rather than retried (TransactionCommitAmbiguousException with a per-source committed/rolled-back breakdown, DbContextFactory.cs:621), and ADR-018's non-SQL engines stay deliberately latent (no production entity on them). Substance grown since (no band move): multi-tenant row isolation ships as a named Tenant global filter with a TenantSaveChangesInterceptor ordered between audit and outbox stamping (ApplicationDbContext.cs:259-271, deliberate interceptor ordering audit → tenant → domain-event → audit-trail at :256-281, filters applied centrally at :333), an opt-in entity change trail (:278-281), a SoftDeleteUniqueIndexConvention (:323), an opt-in DeleteChildren<TChild,TChildId> cascade helper (AuditableAggregateRootEntity.cs:273-292, unit-tested) with a DeleteEntityHandler.Includes/AsTracking eager-load extension point (DeleteEntityHandler.cs:57,63), and the shared RawQueryable/Concurrency convention rules now running against Common's own map in the CI-gated arch tier (RawQueryableConventionTests.cs:13-15, ConcurrencyConventionTests.cs:12-14); that growth, plus keyset (seek) paging ordered by (sortKey, Id) replacing offset counting on read paths (KeysetQueryBuilder.cs:9), now supports the lifted score; the residuals live in the held-at-9 note above. Evidence: Infrastructure/Persistence/DbContexts/ApplicationDbContext.cs:367-381 (ApplySoftDeleteFilters, a named filter, applied at :333), :501-518 (ConfigureConcurrencyTokens, IsRowVersion :514 / IsConcurrencyToken :518), :256,264 (interceptor audit stamping registered from DI); Infrastructure/Persistence/Configuration/EntityTypeConfiguration/EntityTypeConfiguration.cs + EntityTypeConfiguration{SQLServer,Cosmos,Sqlite}.cs; Infrastructure/Persistence/Conventions/CrossDataSourceDegradeConvention.cs:33-35; Application/Specifications/CrossSourceSpecification.cs:39 |
| 9 | API & Contract Design | 2 | 4 | 9 | 8/18 | Consistent RFC 9457 ProblemDetails with centralized status mapping, header-based versioning with a backward-compatible v1.0 default, uniform pagination/filtering/sorting over the generic EntityControllerBase query contract (ADR-034-documented), edge validation, DTO decoupling by ADR, OpenAPI generation (AddCommonOpenApi()/MapCommonOpenApi() over built-in Microsoft.AspNetCore.OpenApi, serving /openapi/v1.json outside Production) plus the opt-in Scalar UI (MapCommonScalarUi(), non-Production only). Request idempotency at the inbound edge is ADR-documented (ADR-017). Contract discipline is enforced automatically in-repo on both axes (the rubric's Maturity-4 bar): (a) an OpenAPI committed-baseline diff normalizes the document a probe host generates through the real AddCommonOpenApi/MapCommonOpenApi pipeline and fails on any change against the committed openapi-baseline.v1.json until it is regenerated deliberately in the same pull request (MMCA_UPDATE_OPENAPI_BASELINE=1), scoped to the framework-owned surface (document-per-version naming, the unbound-route-token backfill, the generated ProblemDetails error schema) because each consumer's concrete API surface stays guarded by that host's own snapshot tier by design (e.g. ADC's OpenApiContractTests); and (b) a dedicated [ServiceContract] purity fitness rule fails the build when a marked contract type reaches into a mapped Domain/Application/Infrastructure namespace, naming the offending type; it is attribute-driven, so it runs as a ratchet in Common (no type carries the attribute here yet) and bites in the consumers. Both projects sit in MMCA.Common.slnx:36,46, so both run in the CI-gated unit tier. Held at Implementation 9: the baseline covers the framework-owned surface only, and the purity rule's in-repo run is vacuous until a *.Contracts type is marked. Evidence: Tests/Presentation/MMCA.Common.API.Tests/OpenApi/OpenApiBaselineTests.cs:45-77 (the diff gate) + OpenApiProbeHost.cs:20 + openapi-baseline.v1.json; Source/Hosting/MMCA.Common.Testing.Architecture/ArchitectureRules.Contracts.cs:32 + Bases/ServiceContractPurityTestsBase.cs:24-26, subclassed at Tests/Architecture/MMCA.Common.Architecture.Tests/ServiceContractPurityTests.cs:11; API/Startup/OpenApiEndpointExtensions.cs:12-19 (the two-level guard recorded), :28,46-50 (MapCommonOpenApi + MapCommonScalarUi); API/Startup/WebApplicationBuilderExtensions.cs:225 (AddCommonOpenApi), :110-128 (header api-version, v1.0 default); Source/Core/MMCA.Common.Shared/Abstractions/ServiceContractAttribute.cs (invariant + ADR-015 purity rules); API/Idempotency/IdempotencyFilter.cs (ADR-017); API/Middleware/ErrorHttpMapping.cs:20-28 |
| 10 | Messaging & Integration Architecture | 3 | 4 | 9 | 12/27 | Scored fresh against the rubric v2 criteria for the first time on the thirty-second wave (2026-09-04): CONFIRMED at M4/I9, so the rebase's "carried at prior pending re-score" caveat retires. Broker topology is by ADR and configuration: Infrastructure/Messaging/BrokerEventBus.cs:31 writes integration events to the outbox and signals the processor instead of dispatching in-process (throwing loudly when a source does not support an outbox), and BrokerMessageBus.cs:9 publishes over MassTransit to RabbitMQ in dev or Azure Service Bus in production (ADR-008/ADR-066). Delivery semantics are per consumer and layered: every receive endpoint gets exponential UseMessageRetry (Infrastructure/DependencyInjection.cs:961) above second-level UseDelayedRedelivery for outages measured in minutes to hours (:957), and FaultIntegrationEventConsumer.cs:8 turns exhausted-retry error-queue rows into a structured Error log plus a broker.fault.count metric without re-throwing. Retention and replay are explicit: OutboxCleanupService.cs:19 purges processed rows past RetentionDays while holding dead-lettered rows for DeadLetterRetentionDays, and IOutboxAdministration.cs:51 exposes ReplayDeadLettersAsync. Consumers are idempotent by construction: EfInboxStore.cs:12 stages the inbox row in the handler's own transaction with a unique MessageId index absorbing concurrent duplicate deliveries (unit-covered by EfInboxStoreTests.cs), and UpcastingIntegrationEventConsumer.cs:13 drains retired contracts through IEventUpcasterRegistry to the terminal contract with dedup still keyed on the original message id (ADR-090). Contract evolution is fitness-enforced (EventVersioningConventionTests.cs and EventUpcasterFitnessTests.cs, both inside MMCA.Common.slnx and run on every pull request, ci.yml:15), and the gateway stays routing plus cross-cutting behavior rather than a second business layer (ADR-008 as amended by ADR-088/ADR-089). Held at 9, not 10: sagas and process managers are a documented deferral (ADR-086), with choreographed compensation plus a persisted marker and a periodic reconciliation sweep (ADR-054) as the accepted interim rather than a shipped orchestrator. 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, feature-gating all handled once in the decorator pipeline; feature-flag management is now ADR-documented (ADR-031: Microsoft.FeatureManagement enforced on both the controller [FeatureGate] surface and the outermost IFeatureGated CQRS decorator, disabled = 404); configuration strongly-typed + ValidateOnStart on seven options types; cache invalidation tied to command success; resilience via one shared Polly mechanism; mapping by ADR; request idempotency centralized in one [Idempotent] filter (ADR-017). The caching substrate is now ADR-documented (ADR-026): application code depends only on ICacheService (Get/Set/Remove/RemoveByPrefix), and AddCaching() binds DistributedCacheService when a real IDistributedCache is present else MemoryCacheService: a startup-time memory-or-distributed swap (the same monolith-now/scale-later extension point as the message bus), with a short default TTL backstopping best-effort prefix invalidation. ↑ Implementation 8→9 (twenty-fifth wave, 2026-08-01): the cross-instance-exclusivity hold-reason is closed in shipped code: IdempotencyFilter resolves an IDistributedLock and falls back to the striped per-process semaphore only when a host registers none, and AddCaching() registers RedisDistributedLock (SET NX PX acquire + compare-and-delete Lua release, real cross-replica mutual exclusion) whenever an IConnectionMultiplexer is present, with the distributed-lock path unit-covered at the filter level and the shipped cache exercised against a real Redis in CI (redis-integration Testcontainers job); ADR-017 revised to document the guard. Held at 9, not 10: prefix eviction stays TTL-only with no multiplexer (explicitly warn-logged), HttpResilienceDefaults values are compile-time constants rather than configuration-bound, and RedisDistributedLock itself is mock-tested only in the unit tier. Evidence: Application/UseCases/Decorators/* (10 decorators); Application/Interfaces/ICacheService.cs; Infrastructure/DependencyInjection.cs:60-246 (.ValidateDataAnnotations().ValidateOnStart()) + :181-195 (RedisDistributedLock-or-InProcessDistributedLock registration); Infrastructure/Concurrency/RedisDistributedLock.cs:36; API/Idempotency/IdempotencyFilter.cs:31-34,109,161-164 (IDistributedLock acquire; striped fallback :67-73,129); Tests/Presentation/MMCA.Common.API.Tests/Idempotency/IdempotencyFilterTests.cs:544 (4 DistributedLock_* facts); .github/workflows/ci.yml:611-647 (redis-integration); Infrastructure/Caching/DistributedCacheService.cs:189 (prefix-eviction warn); Shared/Resilience/HttpResilienceDefaults.cs:13,16,19,28; ADR-026; ADR-017 (revised 2026-08-01) |
| 11 | Security | 3 | 4 | 8 | 12/24 | Authn centralized (JWKS/RS256+HS256 with algorithm pinning), authz at policy layer, secrets validated-not-committed, rate limiting + brute-force protection + security headers + CSP shipped, CI fails on vulnerable packages. Now broadened by a shipped permission-based authorization layer (v1.80.0): [HasPermission("x")] → on-demand perm:x policy via PermissionPolicyProvider → PermissionAuthorizationHandler checks the caller's roles/claims against an immutable IPermissionRegistry of role→permission grants, opt-in and backward-compatible with the existing named role policies, 13 unit tests. The authenticated-only global rate limiter is now ADR-documented (ADR-019). RS256 is pinned on the JWKS-forwarded auth path (v1.82.0): ValidAlgorithms = [RsaSha256] on the forwarded-JWT (JWKS-discovery) validation path, closing the cross-service alg-confusion gap that the in-process pin already covered. Hardened: a framework IPasswordHasher (ADR-032) hashes with PBKDF2-HMAC-SHA512 (32-byte salt, 600k iterations, FixedTimeEquals compare); the formerly load-bearing legacy-HMAC verify-and-migrate branch no longer exists (PBKDF2 is the only supported algorithm and every stored hash is derived and verified through it; prose corrected 2026-08-31), and the always-available brute-force LoginProtectionService is ADR-documented (ADR-029). Recalibrated to Implementation 8 ("Strong", not "Exemplary"): two §11 criteria are unmet in-repo, vault/managed-identity secret binding is delegated to the deployer (correct for a library), and authorization is policy-based RBAC with capability indirection. A real resource-ownership axis now ships and is documented (ADR-033: OwnerOrAdminFilter 403s a single-resource route on a customer_id-claim mismatch, OwnershipHelper row-scopes collection queries, one admin bypass), but it is opt-in per controller, claim-trusting, and ownership-not-ABAC, so it is a real resource-scoped check rather than the full resource/attribute-based policy engine the top band wants. Evidence: Shared/Auth/IPermissionRegistry.cs + PermissionRegistry.cs:10 (FrozenDictionary/FrozenSet, case-insensitive roles); API/Authorization/{HasPermissionAttribute,PermissionPolicyProvider,PermissionAuthorizationHandler}.cs + AuthorizationExtensions.cs:38-42,68; API/Startup/WebApplicationBuilderExtensions.cs:501 (forwarded-path ValidAlgorithms=[RsaSha256], v1.82.0), :651,667 (in-process ValidAlgorithms pinned per RS256/HS256), :61 (IsRateLimitBypassed health/.well-known/grpc, ADR-019), :609 (GetValidatedSigningKey 256-bit floor; anchors re-verified 2026-08-31); Infrastructure/Auth/LoginProtectionService.cs:50-56,74-82 (exp-backoff lockout + per-IP registration limit, ADR-029); Infrastructure/Services/PasswordHasher.cs:9,53 (PBKDF2-only, FixedTimeEquals; legacy branch removed, ADR-032) + Tests/Core/MMCA.Common.Infrastructure.Tests/Services/PasswordHasherTests.cs (11 tests); API/Authorization/{OwnerOrAdminFilter,OwnershipHelper}.cs (resource-ownership axis, ADR-033); Infrastructure/Auth/LoginProtectionService.cs:54-58 (C-1 lockout-backoff-overflow clamp, 2026-07-05); ADR-037 field-level-encryption converter ships but is latent/unadopted |
| 12 | Performance & Scalability | 2 | 4 | 8 | 8/16 | Async-throughout (no sync-over-async), projection/AsNoTracking/split-query/batch-by-IDs primitives + compiled-projection cache that prevent the §12 query red flags, stateless horizontal-scale design, a framework-level max-page-size clamp + unbounded-query ceiling (closing the "unbounded query" red flag), and a repeatable BenchmarkDotNet hot-path harness so hot-path efficiency is measured, not assumed. ↑ Maturity 3→4 (twenty-first wave, restoring the level the nineteenth wave dropped): the latency/allocation regression gate that shipped 2026-07-16 is now a live merge gate. The Performance gate (BenchmarkDotNet Short + baseline verify) job runs the harness with --job Short --exporters json and a second step (build/perfgate) fails against the committed Tests/Performance/perf-baseline.json (deterministic per-benchmark allocation ceilings plus a machine-independent 1000x compiled-cache ratio floor); the job carries no continue-on-error, and its context is present in the live required_status_checks list on main (queried via the branch-protection API this cycle: 8 contexts), so a red perf gate blocks the merge. That is the rubric's automatic-enforcement bar, which the twentieth wave declined only because the context was then absent. A residual doc gap was raised alongside this score and fixed the same day: CONTRIBUTING.md listed five gates and called webkit advisory, and its reproduce snippet omitted the perf gate, webkit, and the Helpdesk canary; all eight contexts are now documented (MMCA.Common PR #100, 658786b). Implementation holds at 8: the baseline gate raises rigor but its scope is one benchmark file (the Specification compiled-cache/composition hot path), the page-size clamp and unbounded-query ceiling are real, and full load/stress timing data still lives in the consumer apps. Evidence: .github/workflows/ci.yml:179 (job name Performance gate (BenchmarkDotNet Short + baseline verify), matching the required context exactly) + .github/workflows/ci.yml:196-204 (--job Short + build/perfgate baseline verify, no continue-on-error); Tests/Performance/perf-baseline.json:3-13 (allocation ceilings + ratio floor); build/perfgate/Program.cs (fails on ceiling/ratio violation); Tests/Performance/MMCA.Common.Benchmarks/SpecificationBenchmarks.cs:14 (spec compiled-cache + composition benchmarks); Application/Settings/ApplicationSettings.cs:15 (MaxPageSize=500) + API/Controllers/EntityControllerBase.cs:127 (clamp) + Application/Services/Query/EntityQueryPipeline.cs:100 (MaxUnboundedResultLimit); Infrastructure/Persistence/Repositories/EFReadRepository.cs:242 (AsNoTracking + split/single-query read primitives) |
| 13 | Observability & Operability | 2 | 4 | 8 | 8/16 | Full OTel (structured logs + RED metrics + distributed tracing) to OTLP and App Insights, liveness/readiness/dependency health checks driving orchestration, correlation IDs, deliberate poll-span noise filtering for cost. Enriched (v1.84.0/ADR-025): a warm-up readiness gate (WarmupReadinessHealthCheck, tagged ready) holds /health/ready unhealthy until startup warm-up has run, so the platform keeps traffic off a warming replica: wired unconditionally into AddServiceDefaults, free for every host, covered by 25 [Fact]/[Theory] across 4 files (count re-synced 2026-08-31). ↓ Implementation 9→8 (twentieth wave, band recalibration, no work lost): two of the six §13 criteria (SLO alerting/dashboards, runbooks) remain the deployer's responsibility, which is the rubric's Strong band ("nearly all criteria met, minor isolated gaps"), not the Exemplary "all criteria met to a high standard": the same deferred-to-consumer calibration §17 and §29 already carry. Maturity holds at 4 (the readiness/health/OTel wiring is baked in, not opt-in, and unit-tested). Re-refuted 2026-09-04 (thirty-second wave, the ninth consecutive refusal of an 8→9 lift): the sample IaC still comments "SLO alerting" at samples/deployment/main.bicep:155 while :156 declares only a Microsoft.Insights/actionGroups resource with no metricAlerts beside it, and the only runbook remains the §29 restore drill. Evidence: Aspire/Extensions.cs:43 (AddWarmupReadiness unconditional in AddServiceDefaults), :164-170 (Outbox/Cqrs meters), :184 (OutboxPollFilterProcessor), :330,334,350 (/health, /alive, /health/ready gate), :285,293 (OTLP + Azure Monitor exporters; anchors re-verified 2026-08-31); Aspire/Warmup/WarmupReadinessHealthCheck.cs:9 + Tests/Hosting/MMCA.Common.Aspire.Tests/Warmup/*Tests.cs (25 test attributes across 4 files); Application/UseCases/Decorators/LoggingQueryDecorator.cs:39 (C-3: query business-failures record outcome=failed, RED-metric parity with the command decorator, 2026-07-05) |
| 14 | Testability & Test Strategy | 3 | 4 | 9 | 12/27 | Right-shaped pyramid (1880 [Fact]/[Theory] across 262 files, weighted to Core); architecture fitness functions enforce dependency/purity/extraction/versioning/PII/specification/slice-cohesion/localization rules, authored once in the shipped MMCA.Common.Testing.Architecture package as 122 methods across 46 abstract bases (ADR-015) and re-run as thin subclasses (Common's own build executes 187, per FACTS.md; counts re-synced 2026-08-31); plus shared test infra is itself a shipped package, and a zero-discovery min-tests guard gates every merge. Recently added (2026-06-30): a translation-coverage fitness gate (LocalizationResourceTests) and a render-snapshot regression tier (PrimitivesSnapshotTests, 5 baselines) via the shipped MarkupSnapshot helper. Prior waves added permission-authz, fixed-TimeProvider, and PiiRedactorTests. CI enforces a 68.3% line-coverage floor that fails the build (unit tier, generated code excluded; ratcheted from 53% by the 2026-07 coverage program, measured ~70.3%). ↑ Implementation 8→9 (sixteenth wave): the fourteenth wave capped this at 8 on a modest 53% coverage floor; that floor is now 68.3% and the suite grew ~300 tests via a coverage-driven program that found and fixed seven real defects (C-1..C-7), so the top band is now supported. Held below 10 by the one remaining Exemplary gap, no mutation testing on the Core tier. Maturity holds at 4 (CI-gated fitness tests + blocking coverage floor + zero-discovery min-tests guard).: Common's own Tests/Architecture/MMCA.Common.Architecture.Tests/{LayerDependency,DomainPurity,MicroserviceExtraction,DependencyVersion,PiiConvention,AggregateConvention,EventVersioningConvention,LocalizationResource,FrameworkSanity}Tests.cs + SpecificationFitnessTests.cs; Tests/Presentation/MMCA.Common.UI.Tests/Components/PrimitivesSnapshotTests.cs; Tests/Presentation/MMCA.Common.UI.Web.Tests/ (Wave C4); .github/workflows/ci.yml:226 (68.3% coverage floor, ratcheted b75fa8f), :63 (min-tests guard) |
| 15 | Best Practices & Code Quality | 2 | 4 | 9 | 8/18 | Five analyzers at error + warnings-as-errors + lock files + NuGetAudit=all (with a blocking CI vulnerability-audit gate), consistent Result-based error handling, narrowly-scoped justified suppressions rather than blanket pragmas, ADRs capturing the why. The formerly-cited dated NuGetAuditSuppress no longer exists: the accepted-advisory list has been empty since the 2026-07-20 sweep (e3c477e, CHANGELOG.md:737-743), a genuine improvement. 9 not 10 (twenty-fifth wave, I9→10 proposal refuted): the repo-wide NoWarn still carries CS1591;RMG020;S8970 plus two conditional blocks, and the surface grew rather than shrank (S8970 added 2026-07-28); three §15-tagged backlog items stay open, two re-verified live in source this cycle (CD-1: GetAllForLookupAsync silently drops its where/orderBy arguments; CD-2: the lookup projection appends an untranslatable ToString()) plus FR-2 (Result<T>.Value returns default on a failed result). Evidence: Directory.Build.props:7-13 (TWAE, lock files, NuGetAudit, audit=all, AnalysisMode=All), :63-84 (Meziantou/VS.Threading/Roslynator/Sonar/StyleCop at error), :22,27,33 (NoWarn entries); .editorconfig:312 (analyzer default = error); .github/workflows/ci.yml:106-129 (Release build + blocking vulnerability audit) |
| 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: Low-coupling/high-cohesion enforced by dual layer gates + extraction fitness functions, explicit versioned package contracts (15 packages), documented upgrade-all-consumers-together policy (no divergent versions), current ADRs (001-078; canonical index in Website docs-src/adr/ since the 2026-07-20 centralization) and a source-generated, CI-drift-gated FACTS.md. Held at 8 because the architecture map (ArchitecturalAnalysis.md) lives in the un-versionable workspace root (this repo carries the in-repo RemediationBacklog.md tech-debt register, but the map cannot be committed here). Evidence: Directory.Build.props:87-88; LayerDependencyTests.cs; release.yml (MinVer git-tag versioning); build/facts/FactsGenerator.cs + .github/workflows/ci.yml:27-28 (FACTS generated + drift-gated); ADRs/README.md kept current with supersession dates |
| 17 | DevOps & Deployment | 2 | 3 | 8 | 6/16 | CI gates build+analyzers+tests+vuln-audit+coverage-floor on every pull request (the workflow is pull_request-only; the former push trigger was removed) and release automates pack/SBOM/publish on tags. Publishing is now dual-registry and keyless (v1.128.0, ADR-053): every release pushes to both GitHub Packages and nuget.org, the nuget.org leg exchanging an OIDC token for a short-lived single-use key via NuGet/login with no stored API key, under a least-privilege permissions block, with a blocking CycloneDX SBOM gate. Now ships an in-repo reference deployment sample: samples/deployment/{foundation,main}.bicep (Container Apps + ACR-via-managed-identity + Key Vault + SQL + cost tags + budget) + DEPLOYMENT.md (OIDC federated-credential setup, UAMI bootstrap, post-deploy smoke-gate/auto-rollback), closing the "no .bicep/.tf anywhere" gap with the reference the scorecard's own remediation named. The sample is kept continuously valid in CI (2026-07-16): a sample-deployment-validate job compile-checks both templates with az bicep build on every pull request, replacing the former point-in-time lint-clean claim. Maturity holds at 3 (twenty-third wave, lift proposal refuted a second time; a 4/9 double-lift proposal refuted again on the thirty-first wave, 2026-09-01, with no §17 source change since 2026-06-27 and only ci.yml anchors moved): the validate job is a compile check, the workflow's own comment states a real what-if/deploy stays a consumer-side concern, and the job's context is absent from the 8 required status checks on main (queried live), so it is not even a merge gate, let alone automatic enforcement of the CD/IaC-apply axis. Held at 8 (not 9): validation is not new deployment execution; it is a reference sample, not a self-deploy (a library can't deploy itself), and full CD machinery lives in consumer repos. A live defect in the reference IaC was surfaced on the twenty-fifth wave and routed to the backlog under #17: samples/deployment/main.bicep:143 binds secretRef: 'sql-conn' but the template declares no secrets array anywhere, so the sample would be rejected at deploy time and az bicep build does not catch it. Evidence: samples/deployment/; .github/workflows/ci.yml:3-16 (pull_request-only triggers), :719-724 (the "a real what-if/deploy stays a consumer-side concern" comment), :735,739 (sample-deployment-validate's two az bicep build steps, compile-only by design, not a required context; anchors re-synced 2026-09-01); .github/workflows/release.yml:13-16 (least-privilege + id-token: write), :53-58 (blocking SBOM gate), :81 (NuGet/login keyless nuget.org trusted publishing, ADR-053; anchor re-synced 2026-09-01); DEPLOYMENT.md:3 (reference sample, explicitly not self-deploying; the former :80 anchor is now a blank line) |
| 18 | UI Architecture & Components | 3 | 4 | 9 | 12/27 | Clean container/presentational split: reusable smart DataGridListPageBase<TDto> + dumb parameterized primitives composed via RenderFragment slots + EventCallback, shared across both consumer apps, gated by bUnit + a gallery render-smoke job. No 1000-line blobs / logic in markup. Evidence: MMCA.Common.UI/Pages/Common/DataGridListPageBase.cs:18; Components/{EmptyState,PageHeader,PageLoadingState,PageErrorState,DeleteConfirmation}.razor; Components/MobileInfiniteScrollList.razor:19,49-52 (typed [Parameter,EditorRequired], EventCallback, @key) |
| 19 | State Management & Data Flow | 3 | 4 | 9 | 12/27 | One explicit owner per state, all per-circuit scoped (no static mutable cross-user state), unidirectional flow via params/EventCallbacks, StateHasChanged marshalled via InvokeAsync, the wrapped-guard stale-IsDirty red flag remediated via IsDirtyAccessor, and the disposed-CTS race fixed (swap-fresh-CTS-before-dispose + catch (ObjectDisposedException), shipped v1.68.0). ↑ Implementation 8→9 (thirtieth wave, 2026-08-31, commit 59d7a97/PR #325): the thirteenth wave's single hold-at-8 reason (fresh state polled/refetched rather than layered behind an explicit client-side staleness policy) is closed in shipped code: v1.175.0 ships IUiReadCache/UiReadCache with a config-bound UiReadCacheOptions policy (Enabled=true by default, 60s default TTL, longest-prefix per-route TTLs), registered unconditionally by AddCommonUI, read-through on all four EntityServiceBase read methods (successes only, explicit bypassCache), prefix invalidation on every successful write, and Clear() on both sign-out paths; plus NotificationState.IsStale/MarkStale now gates the bell's per-navigation refetch behind a configured NavigationRefreshMaxAge. 27 unit tests (15 + 12) live in the CI-gated MMCA.Common.UI.Tests project. Held at 9 (not 10): the cache is an in-memory per-circuit layer (no cross-tab or offline story) and the staleness policy is new this release. Maturity holds at 4 (the guard, disposed-CTS, and now the cache/staleness behaviors are all unit/bUnit-tested inside the CI-gated .slnx). Evidence: MMCA.Common.UI/Services/Caching/IUiReadCache.cs:32,51,59,66 (Get/Set, InvalidatePrefix, Clear); MMCA.Common.UI/DependencyInjection.cs:70-79 (TryAddScoped scoped-state registrations); Services/Notifications/NotificationState.cs:8 (per-circuit scoped state, IsStale/MarkStale); Components/UnsavedChangesGuard.razor:34,36,52 (IsDirtyAccessor/CurrentIsDirty; anchors re-verified 2026-08-31); DataGridListPageBase.cs:553-570 (disposed-CTS swap-before-dispose); Tests/Presentation/MMCA.Common.UI.Tests/Components/UnsavedChangesGuardTests.cs:17 |
| 20 | Design System & UI Consistency | 2 | 4 | 9 | 8/18 | MudBlazor used consistently with a centralized theme/token system, light+dark variants, CI-enforced drift guard binding CSS tokens to the C# source of truth; library quirks fixed once in shared wrappers (grid paging guardrail). The dark palette is now user-toggleable (ADR-028): MudThemeProvider @bind-IsDarkMode driven by a ThemeService (cookie + localStorage + User.PreferredTheme, OS-default), with a ThemeToggle in the shared MainLayout beside the culture switcher (ADR-028 notes the no-flash SSR theme bootstrap is not yet wired). ↑ Implementation 8→9 (eighteenth wave): one of the two deductions that capped this at 8 is closed: the dark palette's filled-primary button label and error-alert text WCAG AA contrast failures are fixed (dark PrimaryContrastText/ErrorContrastText = rgba(0,0,0,0.87)) and locked by a blocking dark-mode axe gate (DarkModeE2ETests in the required chromium ui-e2e job, resolved 2026-07-11). Held below 10 by the second, still-open deduction: some app.css blocks use !important overrides and raw hex outside the token set, and Bootstrap chrome still coexists with MudBlazor. Light mode was already fully AA-gated. Evidence: Theme/MMCATheme.cs:60,73 (dark PrimaryContrastText/ErrorContrastText), :13,45 (light + dark palette); Theme/BrandColors.cs:10-19 (C# SSOT + BrandColorTokenTests.cs:38); Tests/Presentation/MMCA.Common.UI.E2E.Tests/DarkModeE2ETests.cs:30 + .github/workflows/ci.yml:114 (dark-mode axe gate, chromium blocking); ACCESSIBILITY.md:49 (contrast RESOLVED 2026-07-11); UI/Components/ThemeToggle.razor + MainLayout.razor (ADR-028); wwwroot/app.css raw hex/!important (e.g. :122 ⚠ minor) |
| 21 | Accessibility (a11y) | 3 | 4 | 9 | 12/27 | Semantic landmarks, real keyboard-operable controls, ARIA names, stated WCAG 2.1 AA target enforced by a required chromium axe gate in CI covering the loading (named progressbar) and error (alert) component states + shipped E2E a11y bases + a documented manual screen-reader pass (ACCESSIBILITY.md: landmarks/focus-order/ARIA-names/form-error association). Broadening the scan (tenth wave) found and fixed a real WCAG 4.1.2 defect: PageLoadingState wrapped an anonymous progressbar in a bare <div> carrying a prohibited aria-label; now role="status" + a named spinner. ↑ Implementation 8→9 (eighteenth wave): the two documented, deliberately un-gated dark-theme WCAG AA contrast failures that capped this at 8 in the seventeenth wave (filled-primary button label + error-alert message text) are now fixed (dark PrimaryContrastText/ErrorContrastText = rgba(0,0,0,0.87)) and the dark-mode axe scan is a blocking gate (DarkModeE2ETests in the required chromium ui-e2e job), exactly the path back to 9 the prior row named, shipped 2026-07-11. Held below 10 by the standing smaller gaps: no automated focus-trap/reading-order assertion (manual pass only), and axe breadth scoped to the gallery's representative states (deep consumer states scored downstream). Maturity holds at 4 on the required chromium axe merge gate. Evidence: Layout/MainLayout.razor:22,33,88 (skip-nav + role="main" + role="alert"); Components/PageLoadingState.razor:3 (role=status + named progressbar); Theme/MMCATheme.cs:60,73 (dark contrast fix); Tests/Presentation/MMCA.Common.UI.E2E.Tests/DarkModeE2ETests.cs:30 (dark-mode WCAG 2.1 AA axe gate); ACCESSIBILITY.md:44-57 (light+dark AA pass + known limitations: manual-only focus-trap/reading-order); .github/workflows/ci.yml:103-114 (ui-e2e matrix, chromium blocking); Testing.E2E/Infrastructure/AxeOptions.cs:22 (WCAG 2.1 AA tag set); Tests/Presentation/MMCA.Common.UI.E2E.Tests/ComponentsPageE2ETests.cs:82 (loading/error-state axe coverage) |
| 22 | Responsive & Cross-Browser | 2 | 4 | 9 | 8/18 | Fluid MudBlazor breakpoints, explicit grid→card mobile alternative, a 3-engine cross-browser matrix in CI, density options on DataGridListPageBase (a DenseGrid property + ToggleDensity() round-tripping through ListPageState: URL key d, in-memory, sessionStorage; unit-tested in ListPageStateServiceTests/ListPageQueryStateServiceTests), the shared .mmca-touch-target 48px affordance (cart drawer + mobile cards + data-grid pager, enforced by a phone-viewport Playwright bounding-box test), and a documented RESPONSIVE.md device/breakpoint matrix (all twelfth-wave Implementation-8 evidence, unchanged). ↑ Maturity 3→4 (nineteenth wave): firefox was promoted to a required merge gate alongside chromium (2026-07-12), clearing the chromium-only red flag. ↑ Implementation 8→9 (twentieth wave): webkit was promoted to a required merge gate on 2026-07-16 after 11 consecutive green main runs, so all three engines now block merges (confirmed live via the branch-protection API: the chromium, firefox, and webkit ui-e2e contexts are all in required_status_checks), closing the single stated hold-at-8 reason ("webkit remains advisory") and completing the 3-engine matrix as an enforced capability. Held below 10: the matrix runs against the backend-less gallery's representative states, and RESPONSIVE.md's device rows are a documented matrix, not device-lab execution. (Doc drift cured with this refresh: RESPONSIVE.md still listed webkit as advisory.) Evidence: .github/workflows/ci.yml:111-114 (3-engine matrix, all required merge gates; webkit promotion note 2026-07-16; no continue-on-error in the job); MMCA.Common.UI/Pages/Common/DataGridListPageBase.cs (DenseGrid/ToggleDensity) + Services/ListPageStateService.cs/ListPageQueryStateService.cs (DenseGrid persisted); wwwroot/app.css:148 (.mmca-touch-target shared rule); RESPONSIVE.md (breakpoint + browser matrix); Tests/Presentation/MMCA.Common.UI.E2E.Tests/ComponentsPageE2ETests.cs (touch-target viewport test); Common/BreakpointConstants.cs:16 (<960px mobile) |
| 23 | Front-End Performance | 2 | 4 | 8 | 8/16 | Server-side paging (no load-everything-then-page-in-memory), cancellation of superseded fetches, SSR persist/restore to kill a redundant round-trip, bounded infinite-scroll DOM (MaxRenderedItems cap), @key-stable rendering, skeleton/loading states. ↑ Maturity 3→4 (eighteenth wave, applied to this table on the nineteenth): the conventions the thirteenth wave recalibrated to review-enforced are now measured AND automatically enforced: WebVitalsE2ETests asserts LCP/TTFB/CLS budgets on the gallery Login + Components pages (via the shipped WebVitalsCollector, no longer unwired) inside the required blocking chromium ui-e2e merge gate, meeting the rubric's maturity-4 "enforced automatically (CI)" bar. (The eighteenth-wave prose recorded this move but the table row was never updated; this refresh cures that inconsistency.) Implementation holds at 8 (thirtieth wave, 2026-08-31, proposed 8→9 refuted; the row's former sole cap is rewritten): the desktop-grid virtualization gap IS closed at v1.175.0 (opt-in MudDataGrid row virtualization ships, DataGridListPageBase.cs:140,606 with a VirtualizeServerData funnel, adopted by the gallery /grid page over 1,000 rows and proven in-browser by E2E facts asserting the rendered window stays under 200 rows, GridPageE2ETests.cs:41-73), but three criteria hold the score in Strong: (1) asset hygiene, the packaged MMCA.Common.UI wwwroot ships 1.4 MB of static assets including a 434 KB unreferenced app-specific speaker PNG (wwwroot/images/speakers/miguel-wood.png, zero references repo-wide) and a 576 KB Bootstrap CSS source map, with no wwwroot Content Remove in the csproj; (2) initial load, no lazy-loading/code-splitting anywhere in Source and no bundle/payload budget in CI; (3) measurement, INP is explicitly not measured (WebVitalsE2ETests.cs:24-27) and the gate's ceilings (LCP/FCP 8000ms, TTFB 4000ms, CLS 0.25) are 3-5x the package's own good-band defaults (WebVitalsBudgetTests.cs:21-25), a catastrophic-regression floor rather than a budget. Evidence: DataGridListPageBase.cs:140,606 (VirtualizeGrid opt-in + LoadVirtualizedServerDataAsync); Tests/Presentation/MMCA.Common.UI.Gallery/Pages/GridGallery.razor:44 (gallery adoption); Tests/Presentation/MMCA.Common.UI.E2E.Tests/GridPageE2ETests.cs:43 (bounded-window proof) + WebVitalsE2ETests.cs:56,66 (CWV budget on /grid, non-vacuous guard); .github/workflows/ci.yml:228 (3-engine ui-e2e matrix, required contexts); DataGridListPageBase.cs:403-409 (ServerData + ResetCancellationTokenAsync), :109-138,413-428 (PersistentComponentState); MobileInfiniteScrollList.razor:61,97 (MaxRenderedItems cap, IntersectionObserver) |
| 24 | Forms, Validation & UX Safety | 2 | 4 | 8 | 8/16 | Unsaved-changes guard with reliable current-dirty reads, submit/loading/error/empty states, destructive-action confirmation, genuine client/server password-rule parity, now with field-level error association: Register/Login are EditForm + DataAnnotationsValidator + per-field ValidationMessage over typed models (RegisterModel/LoginModel, PasswordComplexity attribute mirroring the server rule), with the MudAlert kept for form-level/server errors. This closes the prior red flag (a generic summary not tied to the offending input). ↑ Maturity 3→4 (fourteenth wave): the shared auth-form convention is enforced automatically in the CI-gated .slnx unit tier (AuthModelValidationTests, 8 facts covering required/email/complexity/password-match; RegisterFormTests asserting the per-field ValidationMessage renders on an empty submit and the auth service is never invoked), meeting the rubric's maturity-4 "enforced automatically by tests/CI" bar, consistent with §19's M4 on the same CI-gated guard tests. (The shared FormsConventionTestsBase stays consumer-scoped: it enumerates Source/Modules/**/*Create.razor, absent here, and Common has no subclass, so the in-repo enforcement rests on these bUnit/model tests.) Held at 8 (not 9): only the shared auth forms live here; consumer module forms are scored downstream. Evidence: Pages/Auth/{Register,Login}.razor (EditForm + For= per-field) + {RegisterModel,LoginModel,PasswordComplexityAttribute}.cs; Tests/.../Pages/Auth/{AuthModelValidationTests,RegisterFormTests}.cs; Components/UnsavedChangesGuard.razor |
| 25 | Navigation & Information Arch | 2 | 4 | 9 | 8/18 | Route-level auth via AuthorizeRouteView (not just UI hiding), role/claim-filtered navigation, focus management on navigate, query-state preservation, and both 404 and 403 handled in the app shell: a dedicated Forbidden (403) page renders for the authenticated-but-unauthorized branch (was a bare MudAlert), alongside the existing NotFound. A NavigationFlow.md documents the Common UI surface's routes + role/claim model (mirroring ADC's). ↑ Maturity 3→4 (twentieth wave): the navigation-contract drift gate the prior risk list prescribed now ships and gates merges: NavigationContractTests reflects over every routable MMCA.Common.UI page (RouteAttribute/AuthorizeAttribute) and asserts route set-equality plus auth-posture agreement against the embedded NavigationFlow.md routes table, with a non-vacuous 8-route floor; the test project is in MMCA.Common.slnx, so the gate runs in the CI-gated unit tier, meeting the rubric maturity-4 "enforced automatically (CI)" bar and closing the §25 red flag (navigation drifting from the documented per-role design). ↑ Implementation 8→9 (thirtieth wave, 2026-08-31, commit 59d7a97/PR #325): the hold-at-8 reason ("parameters typed and validated" structurally unexercised, all routes parameterless string templates) is closed by shipped work: a typed, constrained deep-link route now ships (@page "/notifications/inbox/{Id:int}", Pages/Notifications/NotificationInbox.razor:2) with the :int constraint documented as the validation boundary (a malformed id renders NotFound, NavigationFlow.md:21). Held at 9 (not 10): one typed route exercises the criterion; broader deep-link breadth is consumer-side. Evidence: Pages/Notifications/NotificationInbox.razor:2 (typed constrained route); NavigationFlow.md:21 (validation-boundary documentation); Tests/Architecture/MMCA.Common.Architecture.Tests/NavigationContractTests.cs:29,44 (set-equality + auth-posture drift gate) + MMCA.Common.Architecture.Tests.csproj:12 (NavigationFlow.md embedded resource); MMCA.Common.slnx:46 (gate in the CI-gated unit tier); Pages/Forbidden.razor + Routes.razor:11 (AuthorizeRouteView: NotAuthorized→<Forbidden/>, NotFoundPage); Components/RedirectToLogin.razor (ReturnUrlProtector); Tests/.../Pages/ForbiddenTests.cs |
| 26 | Front-End Security | 3 | 4 | 9 | 12/27 | No unsanitized HTML/eval (grep MarkupString|innerHTML|eval over MMCA.Common.UI = zero), centralized hardened security-headers middleware with a pluggable ICspPolicyProvider CSP extension point (now ADR-023-documented, replacing per-host hand-rolled headers), unit-tested open-redirect protection, explicit "client untrusted / server re-validates" posture, no client-shipped secrets, and access tokens held in-memory with the refresh token in an HttpOnly cookie (no localStorage; the comments in ITokenStorageService explicitly state tokens are NOT stored there: the earlier "localStorage" finding was a stale doc artifact, corrected in commit 363861d). The SSR token-storage scheme (HttpOnly access/refresh cookies seeded at login, an SSR-time non-validating read handler for [Authorize] on fresh GETs, refresh-never-leaves-the-server) is now documented in ADR-022 (a BFF-style design whose security argument rests on the API remaining the validation boundary). ↑ Implementation 8→9 (thirtieth wave, 2026-08-31, shipped by 363861d "R18 §26: harden default CSP" and carried into 59d7a97/PR #325): the six-cycle hold reason (the default static CSP deliberately omitting script-src/style-src) is closed: SecurityHeadersSettings.ContentSecurityPolicy now defaults to the complete hardened baseline default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none', string-pinned by a unit test in the CI-gated .slnx, and a per-request {nonce} facility ships alongside it (placeholder substitution + CspNonce.Get, tested in enforce and report-only modes). Held at 9 (not 10): style-src retains 'unsafe-inline' for the MudBlazor inline-style reality, and the nonce path is opt-in for hosts. Maturity holds at 4 (the hardened baseline, HSTS, and nonce behaviors are unit-tested). Evidence: Aspire/Security/SecurityHeaders.cs:53-55 (complete default CSP with both directives; rationale at :39-51), :32-35,128 (HSTS), :64-68 (ICspPolicyProvider extension point) + :157-177 (AddCommonSecurityHeaders, TryAddSingleton default → first-registered provider wins); Tests/Hosting/MMCA.Common.Aspire.Tests/Security/SecurityHeadersMiddlewareTests.cs:95-101 (hardened-baseline pin), :122-133,140-166 (per-request {nonce} substitution, enforce + report-only), ADR-023; ITokenStorageService.cs:5-6; Services/Navigation/ReturnUrlProtector.cs:18-60 + tests |
| 27 | Internationalization (i18n) | 1 | 4 | 9 | 4/9 | ↑ Maturity 3→4 + Implementation 8→9 (fifteenth wave, i18n completion train, ADR-027 Decision 9). Multi-locale i18n (en-US + Spanish) ships in the framework itself and default-on: co-located .resx + IStringLocalizer<T>, backend errors localized server-side at the edge keyed by the existing Error.Code (English Message as fallback), one culture cookie as the source of truth across SSR/Server/WASM (forwarded to services as Accept-Language), and a User.PreferredCulture persisted per user with a CultureSwitcher in the shared layout. Maturity is now Optimized because ALL of it is machine-enforced: the translation-coverage gate (LocalizationResourceTests, now with a non-vacuous MinimumBaseResources = 3 floor) fails the build on a missing/incomplete es sibling; the NEW literal gate (LocalizedTextConventionTests, a LocalizedTextConventionTestsBase subclass) fails the build on hard-coded snackbar/title/<PageTitle>/breadcrumb/NavItem literals; MA0076 culture-less formatting is a build error; and the pseudo-localization pass is a REQUIRED CI gate, not a diagnostic (PseudoLocalizationE2ETests in the chromium ui-e2e merge gate renders /login, /register, /components under qps-Ploc asserting the [!! sentinel round-trip AND no horizontal overflow under ~40% expansion, the rubric's layout-tolerance criterion, plus an en-US leak guard). Implementation 9: the framework's own chrome is fully externalized (SharedResource 136 keys en+es: NavMenu, auth pages, error/empty/loading states, ReconnectModal, notification pages, UI.Web SSR Error page), MudBlazor built-in chrome localizes via ResxMudLocalizer over MudTranslations.{resx,es.resx} (all 145 MudBlazor 9.6.0 keys, DI-resolution-tested), NavItem.TitleResource makes module nav menus culture-aware, the ErrorMessages.Success fragment-concatenation red flag is [Obsolete] (whole-sentence page keys), and Common.Error.* templates no longer surface raw ex.Message. Held below 10: two locales, no RTL. Evidence: Tests/Presentation/MMCA.Common.UI.E2E.Tests/PseudoLocalizationE2ETests.cs; Tests/Architecture/MMCA.Common.Architecture.Tests/{LocalizedTextConventionTests,LocalizationResourceTests}.cs; Source/Hosting/MMCA.Common.Testing.Architecture/{ArchitectureRules.LocalizedText.cs,Bases/LocalizedTextConventionTestsBase.cs}; Source/Presentation/MMCA.Common.UI/Globalization/ResxMudLocalizer.cs + Resources/MudTranslations.{resx,es.resx} + DependencyInjection.cs (TryAddTransient + Pseudo decorator); Pages/Common/ErrorMessages.cs ([Obsolete] Success, sanitized templates); Common/NavItem.cs (TitleResource); Tests/Presentation/MMCA.Common.UI.Gallery/GalleryHost.cs (test-only qps-Ploc allowlist); ADRs/027-multi-locale-i18n.md §8/§9 |
| 28 | Front-End Testing & Quality | 3 | 4 | 9 | 12/27 | Right-shaped pyramid (many bUnit component + service unit tests, fewer Playwright E2E), a shipped shared E2E package reused across apps, axe WCAG 2.1 AA + render-smoke as a real-browser CI merge gate against a self-hosted gallery, robust role/test-id selectors with auto-wait (no sleep-flakiness), per-test trace artifacts. ↑ Implementation 8→9 (2026-06-30): the prior "no visual-regression layer" gap is closed by a render-snapshot (golden-markup) regression tier (MarkupSnapshot, shipped in Testing.UI for consumer reuse) that normalizes per-render MudBlazor GUIDs and diffs the markup of shared primitives against committed baselines, failing the build on an unintended structural change. It is deterministic and OS-independent (markup, not pixels), so it runs in the in-solution unit tier on every platform with no per-platform golden management. Held below 10: it is a markup-snapshot, not pixel visual-regression, and E2E journeys still center on Identity/auth. Evidence: Source/Hosting/MMCA.Common.Testing.UI/Infrastructure/MarkupSnapshot.cs; Tests/Presentation/MMCA.Common.UI.Tests/Components/PrimitivesSnapshotTests.cs + Snapshots/*.html; Testing.E2E/** (PageObjects, abstract *TestsBase); Infrastructure/PageExtensions.cs:113-132,185-210 (FillAndVerify auto-wait + axe); .github/workflows/ci.yml:72-135 (ui-e2e gate, chromium blocking) |
| 29 | Resilience & Business Continuity | 3 | 4 | 8 | 12/24 | Failure-isolation is strong and fitness-enforced (timeouts/retries/circuit breaker on every outbound client + outbox graceful degradation), the chaos/fault-injection test trips a breaker and asserts BrokenCircuit short-circuiting, the warm-up/readiness gate is unit-tested (gate latch/idempotency/thread-safety, opens even on task failure), and OpenIdConnectMetadataWarmupTask pre-warms OIDC to kill ACA cold-start. ↑ Maturity 3→4 (2026-06-30): the in-repo restore drill (DatabaseRestoreDrillTests: seed → backup → catastrophic wipe → restore → verify on ephemeral SQLite) runs on every CI build in the unit tier (a build gate, not a scheduled cron (the distinction that keeps a non-gating drill at M3)) and RESILIENCE.md records the framework's measured restore baseline (~5 ms median RTO over 5 runs, 0-row RPO byte-for-byte asserted) alongside the SLO/error-budget template. The recovery procedure is thus demonstrated, measured, and automatically enforced in-repo, meeting the M4 "enforced automatically by tests/CI" bar. Implementation held at 8: production RTO/RPO against real cloud backups + measured production SLOs remain consumer IaC. Evidence: Tests/Core/MMCA.Common.Infrastructure.Tests/Resilience/DatabaseRestoreDrillTests.cs; RESILIENCE.md (measured baseline + SLO template); Aspire/Warmup/ + Tests/Hosting/MMCA.Common.Aspire.Tests/Warmup/*Tests.cs; Tests/.../ResilienceCircuitBreakerFaultInjectionTests.cs:17-61 (ADR-009/025) |
| 30 | Compliance, Privacy & Governance | 2 | 3 | 8 | 6/16 | Directly answers the headline §30 red flag (soft-delete-as-only-deletion-path) with an explicit erasure extension point, AES-256-GCM encryption, bounded retention, and a build-gating PII fitness function: now also closing the second §30 red flag ("PII in logs/telemetry"): the shipped, tested PiiRedactor (v1.84.0) masks every [Pii]-marked member (shallow, value-erasing) before an entity carrying personal data is written to a structured log or telemetry attribute, the redaction half of the [Pii] contract the docs previously only advertised. The PII guard is no longer vacuous: a PiiErasureContractFitnessTests build gate now forces a [Pii]-marked DataSubjectSample through PiiRedactor + IAnonymizable end-to-end, closing the prior cycle's "no fitness function forces types through the redactor" blocker. Broadened 2026-08-13/14 by real shipped DSAR + audit-trail substance: a DataExportControllerBase DSAR export surface behind the Privacy.DataExport feature flag (ADR-076), a field-level audit trail whose interceptor records the redaction token for [Pii] members on both sides of every change (AuditTrailSaveChangesInterceptor.cs:309-310, CI-gated by a unit test) with a 90-day retention purge job (ADR-075), and a shared erasure workflow that soft-deletes then irreversibly anonymizes in place (DeleteUserHandlerBase.cs:103), so the old "DSAR endpoints and retention execution are consumer-resident" leg of the rationale no longer holds as written. Maturity held at 3 (not lifted to 4) after adversarial review, fifth consecutive cycle: the new surfaces are opt-in twice over (AuditTrailSettings.Enabled defaults false, the purge runs only if the host also runs the scheduler; the export is off until a host enables the flag), and no new automatic category-level gate landed: the only two §30 gates remain the structurally vacuous PiiConventionTests scan and the single-sample PiiErasureContractFitnessTests mechanism test, while the remaining governing process (the personal-data inventory, consent capture, residency verification) is absent in-repo or consumer-resident, so the category as a whole is not automatically enforced (the rubric's M4 bar). Implementation held at 8: consent/lawful basis is entirely absent, no PII-inventory artifact exists (no PRIVACY.md here), and the CSV export row-scoping hook is fail-open by default with no fitness function forcing an override (EntityControllerBase.cs:542, default null, warning prose :215-218,528-530; anchor re-verified 2026-08-23). Same "deeper substance deferred" calibration as §11.: ADR-005; Domain/Privacy/PiiRedactor.cs:24-142 ([REDACTED] token, per-type reflection cache); Tests/Core/MMCA.Common.Domain.Tests/Privacy/PiiRedactorTests.cs (7 [Fact]s, incl. "never emits the clear-text PII values"); Tests/Architecture/.../PiiErasureContractFitnessTests.cs:19-40 (forces a [Pii] type through redactor + anonymize); Domain/Interfaces/IAnonymizable.cs:22-31 (idempotent Result-based Anonymize); Infrastructure/Persistence/Encryption/EncryptedStringConverter.cs:31,34 (AES-256-GCM, 12B nonce/16B tag, now ADR-037-documented; shipped but unadopted/latent, no production column encrypted) |
| 31 | Cost Efficiency / FinOps | 2 | 2 | 8 | 4/16 | Real design-level cost awareness on the telemetry/poll-interval axis (OutboxPollFilterProcessor + idle-vCPU socket tuning + a released cost guide), with in-framework execution levers rather than docs alone: a built-in head-based OTel trace sampler knob (Telemetry:TracesSampleRatio → ParentBasedSampler(TraceIdRatioBasedSampler), unit-tested, the largest trace-ingestion lever) and the outbox per-message "dispatched" log moved Info→Debug (caps the highest-volume log line). ↑ Implementation 7→8 (thirty-second wave, 2026-09-04): health-probe traffic no longer bills. The default-ON Telemetry:FilterProbeTelemetry knob wires ProbeTelemetryFilter into both the ASP.NET Core and HttpClient tracing instrumentation (Aspire/Extensions.cs:224), and a ProbeTelemetryFilterProcessor registered before the exporters un-records the probe-child dependency spans the inbound filter cannot reach, the health check's SQL SELECT 1 and the Redis PING (Extensions.cs:248, Aspire/Telemetry/ProbeTelemetryFilterProcessor.cs:20); metrics are deliberately untouched, so probe traffic stays on dashboards. It shipped in v1.182.0, the release the CHANGELOG titles "Cost release" (CHANGELOG.md:136), after v1.181.0 made Telemetry:DisableHttpClientMetrics and DisableRuntimeMetrics actually hold under UseAzureMonitor() by registering MetricStreamConfiguration.Drop views (CHANGELOG.md:187). Held at 8 (not 9): right-sizing and reversible scale-events remain consumer-side actions the framework only recommends, and cost attribution plus budgets exist only as compile-only sample IaC. Maturity holds at 2: no cost/budget/FinOps convention is enforced by CI or review anywhere in-repo (zero matches over Tests/Architecture, no cost gate in any workflow). Evidence: Aspire/Extensions.cs:224,248 + Aspire/Telemetry/ProbeTelemetryFilterProcessor.cs:20 + Tests/Hosting/MMCA.Common.Aspire.Tests/Telemetry/*Tests.cs (27 [Fact]/[Theory] across ProbeTelemetryFilterTests 12, ProbeTelemetryFilterProcessorTests 11, ProbeTelemetryToggleTests 4; project at MMCA.Common.slnx:46); Aspire/Extensions.cs (TracesSampleRatio sampler) + Tests/.../Telemetry/TracesSampleRatioTests.cs; Infrastructure/.../Outbox/OutboxProcessor.cs (LogMessageProcessed=Debug); Aspire/Telemetry/OutboxPollFilterProcessor.cs:34-45; common-COST.md:69 (right-sizing framed as a deployment choice; path corrected 2026-09-04, the in-repo COST.md no longer exists); samples/deployment/main.bicep:166 (conditional budget + costCenter tags, compile-only in CI) |
| 32 | Dependency & Supply-Chain | 3 | 4 | 9 | 12/27 | Hits essentially every §32 criterion for a published framework: central pinned versions, 26 lock files + audit=all gated in CI across feeds, source-mapped trusted feed, SemVer via MinVer, CycloneDX SBOM a hard release gate, Apache-2.0 license (patent grant; committed LICENSE file), and (uniquely) a fitness function (ADR-016) turning the licensing-driven MassTransit-v8 pin into a build gate so the 'blanket update reintroduces known-bad dependency' red flag cannot recur. Now reinforced by a committed .github/dependabot.yml (weekly NuGet + Actions PRs) carrying version-update:semver-major ignores for every held package (MassTransit, VS.Threading.Analyzers, StackExchange.Redis, MessagePack) (so the pin discipline lives in config, not only in the test) plus a periodic sweep to latest stable. Minor gap only: the coordinated consumer sweep is policy, not automated. Evidence: Directory.Packages.props:54-56 (MassTransit 8.5.5 pinned + v9 commercial-license-crash note :63); Directory.Build.props:8-10; Tests/Architecture/.../DependencyVersionTests.cs + base DependencyVersionTestsBase.cs:24-37 (fails if MassTransit major≥9); .github/dependabot.yml:24-35 (held-package semver-major ignores); release.yml:45-50 (SBOM, blocking); 26 packages.lock.json; ADR-016; ADR-038 (supply-chain provenance) records the layered posture in one decision record, noting the SBOM is generated but not yet signed or attested (a possible follow-up, so I9 holds rather than moving to 10) |
| 33 | Developer Experience & Inner Loop | 2 | 4 | 9 | 8/18 | Strong inner loop: documented, gitignored cross-repo source override (the rubric's explicit local.props example), Aspire one-command run for consumers, one-obvious-command build/test/pack, analyzer/editorconfig parity local-to-CI, and a 1025-line, 9-phase GETTING-STARTED.md adoption guide (Phase 0 prerequisites → Phase 8 extract-a-module) with MMCA.Helpdesk as a worked, runnable companion. ↑ Maturity 3→4 + Implementation 8→9 (twentieth wave): the consumer-source-build canary (builds MMCA.Helpdesk against the PR's framework source via UseLocalMMCA) was promoted from advisory to a required merge gate on 2026-07-16: continue-on-error removed and the "Consumer source build (Helpdesk)" context added to branch protection after 9 consecutive green runs (ci.yml:267-268 promotion note; confirmed live via the branch-protection API and CONTRIBUTING.md:62). A framework change that breaks a source-mode consumer now blocks the merge automatically, clearing both the maturity-4 automatic-enforcement bar and the sole advisory-canary cap the nineteenth wave placed on Exemplary implementation. Held below 10 by residual friction, now implementation notes only: the Aspire AppHost hangs when launched headless (must be run interactively) and Common itself is a library, not independently runnable. (Resolved by PR #100, verified 2026-07-23: the CONTRIBUTING.md:57-66 prose list and the :113-123 reproduce snippet now carry all eight required contexts incl. webkit, the perf gate, and the Helpdesk canary.) Evidence: .github/workflows/ci.yml:267-303 (required cross-repo canary, no continue-on-error); CONTRIBUTING.md:62 (required-checks list incl. "Consumer source build (Helpdesk)"); the published getting-started guide (9 phases); workspace CLAUDE.md (local.props.template → UseLocalMMCA → ../MMCA.Common/Source, gitignored); MMCA.Common/CLAUDE.md (build/test/pack/single-test/arch-test commands) |
| 34 | Architecture Governance & Docs | 2 | 4 | 8 | 8/16 | Reference-quality governance: one hundred six ADRs (001-106, canonical in Website docs-src/adr/; the per-ADR enumeration here stops at 078, with 079-106 indexed in docs-src/adr/README.md, the source of truth for count/range; incl. ADR-015 architecture fitness functions, ADR-016 lockstep versioning + MassTransit pin, ADR-017 request idempotency, ADR-018 polyglot persistence, ADR-019 layered rate-limiting, ADR-020 permission-based authorization, ADR-021 consumer-inbox idempotency, ADR-022 browser session-cookie auth, ADR-023 centralized security-response headers, ADR-024 two-channel notifications, ADR-025 startup warm-up + readiness gating, ADR-026 two-tier caching strategy, ADR-027 multi-locale i18n (supersedes 011), ADR-028 day/dark theme, ADR-029 brute-force protection, ADR-030 startup sole-migrator, ADR-031 feature-flag management, ADR-032 password hashing, ADR-033 resource-ownership authorization, ADR-034 generic entity controllers + dynamic query contract, ADR-035 optimistic concurrency, ADR-036 external OAuth login, ADR-037 field-level encryption at rest, ADR-038 supply-chain provenance, ADR-039 live channel push, ADR-040 authenticated output caching, ADR-041 observability and telemetry, ADR-042 MAUI device-capability abstraction, ADR-043 mobile deep links + native OAuth callback, ADR-044 native push delivery, ADR-045 managed file storage + avatars, ADR-046 HTTP API versioning, ADR-047 soft-deleted user session revocation, ADR-048 primitive identifier type aliases, ADR-049 library-scoped ConfigureAwait(false) policy, ADR-050 JWT refresh-token rotation with reuse detection, ADR-051 client-side auth token lifecycle across render modes, ADR-052 background job execution, ADR-053 dual-registry package publishing, ADR-054 choreographed saga compensation with a reconciliation backstop, ADR-055 repository plus specification as the data-access contract, ADR-056 Blazor render-mode strategy for the web heads, ADR-057 expand/contract schema evolution enforced as a CI gate, ADR-058 runtime conformance suites shipped as a package, ADR-059 the IModule contract and reflection-based module composition, ADR-060 performance-regression gate with a committed benchmark baseline, ADR-061 runtime secret management, ADR-062 SLO alerting as code, ADR-063 accessibility conformance gate, ADR-064 deploy recency gates, ADR-065 scaffolding templates, ADR-066 broker transport selection, ADR-067 UI module shell composition, ADR-068 value objects as validated primitives, ADR-069 shared data-protection key ring, ADR-070 fail-fast configuration contract, ADR-071 barcode scanning + QR display, ADR-072 QR badge check-in + points gamification, ADR-073 multi-tenancy (shared-schema + DB-per-tenant), ADR-074 recurring job scheduler, ADR-075 audit trail (field-level change history), ADR-076 data-subject export (DSAR) contract, ADR-077 HybridCache substrate (amends 026), ADR-078 CSV export as a dedicated endpoint) with index + authoring guide, each non-obvious decision matched to a fitness function; architecture rules enforced automatically twice over (compile-time MSBuild target + a now-shared NetArchTest rule library, ADR-015) plus dependency-version/PII/aggregate/event-version/specification/slice-cohesion fitness functions; a 504-line getting-started guide (Website/docs-src/guides/common-GETTING-STARTED.md, canonical in the Website repo) + current CLAUDE.md + the ArchitecturalAnalysis map. FACTS.md is generated from source by a dependency-free build/facts tool and CI drift-gated (a --check step fails the build when the committed facts drift), so version / package-count / ADR-range / fitness counts are a computed-and-gated artifact rather than hand-maintained prose; the rubric (ArchitectureEvaluationCriteria.md) and FACTS.md are version-controlled in this repo, completing the §34 pattern already applied to the ADRs and this two-axis scorecard. ↓ Implementation 9→8 (thirteenth wave), held at 8 (fourteenth wave, re-confirmed nineteenth): Strong, not Exemplary, held down by the one structural residual: ArchitecturalAnalysis.md lives in the uncommittable workspace root (the root can't be versioned). Transient caveat at the nineteenth-wave re-score: the tree is dirty again (ADRs 046-048 untracked plus a modified FACTS.md, an ADR-adding pass in flight), the recurring per-cycle hygiene nit tracked in the backlog; the sixteenth-wave adversarial precedent stands (a between-refresh prose lag is transient and cured by each refresh, and the durable cap is the uncommittable map, not the lag). Held at 8 on the thirty-first wave (2026-09-01): a proposed 8→9 lift was refuted because no §34 machinery landed since v1.175.0, the uncommittable-map cap is unchanged (the published ADR index still points deep mechanics at the workspace-level map, docs-src/adr/README.md:121), and the living documentation now trips the rubric's own red flag by contradicting the gated facts: the workspace map states the framework grew to 13 published packages (Docs/Architecture/ArchitecturalAnalysis.md:5) and the onboarding primer restates fifteen (docs-src/onboarding/00-primer.md:15,243) against 17 at FACTS.md:19; routed to the backlog under #34. Evidence: ADRs 001-106 (Accepted/dated; Website docs-src/adr/README.md index + 'Writing a new ADR' guide); build/facts/FactsGenerator.cs + .github/workflows/ci.yml:97-100 (FACTS generated + --check drift gate, deliberately not code-guarded so it runs on docs-only PRs; anchor re-synced 2026-09-01) + FACTS.md; ArchitectureEvaluationCriteria.md (rubric in-repo); Source/Build/MMCA.Common.LayerEnforcement.targets AND Tests/Architecture/.../*Tests.cs (subclassing Source/Hosting/MMCA.Common.Testing.Architecture); Website/docs-src/guides/common-GETTING-STARTED.md |
Weighted column = Maturity·weight / Implementation·weight per row. Axis-gap findings: §17 is mature-but-execution-deferred (mechanism shipped, deeper proof lives downstream); §8 left that group on the thirtieth wave (2026-08-31) when its CI migration-apply gate landed and Implementation reached 9; §13 joined that calibration on the twentieth wave (Implementation 8: alerting/dashboards/runbooks are deployer-owned); §29 closed its maturity gap (the restore drill is now a build gate with a measured baseline) but its Implementation 8 still defers production SLOs downstream; §30/§31 are the inverse (in-repo implementation leads a maturity capped by consumer-owned governance/cost process); §12 left that group this cycle when its perf gate became a required check, and now sits mature-but-execution-deferred like §17/§8 (load and stress timing against realistic volumes is a consumer-app concern). §10 left the under-9 group on the twenty-fifth wave when its distributed idempotency lock shipped. Together these are the structural source of the 97.5% vs 86.2% gap; §9 left the review-enforced group on 2026-08-22 when its two in-repo contract gates landed.
Indices
- Maturity index = Σ(maturity×weight) ÷ Σ(weight×4) = 312 ÷ 320 = 97.5% (unchanged on the thirty-second-wave full re-score, 2026-09-04, at v1.185.0: no maturity value moved, §17 3→4 and §30 3→4 were refuted once more (§30 for the eighth consecutive cycle) and §31 held at 2 because no cost convention is enforced by CI or review anywhere in-repo; rubric v2 rebase, 2026-09-04: §10 carried at M4 but re-weighted 2→3 (+4), §16 N/A removes 4×2 (−8), Σweight 81→80, no score moved; prior figure 316 ÷ 324 = 97.5%, up from 96.9% (314/324) on the targeted 2026-08-22 update: §9 API & Contract Design Maturity 3→4 on weight 2, 314 + 2 = 316, after the in-repo OpenAPI baseline diff and the dedicated
[ServiceContract]purity rule landed in MMCA.Common PR #271; re-confirmed on the twenty-eighth-wave full re-score, 2026-08-23, which also refuted a proposed §30 Maturity 3→4; re-confirmed again on the twenty-ninth-wave full re-score, 2026-08-31, which refuted three further maturity proposals: §17 3→4, §30 3→4 for the sixth consecutive cycle, and §31 2→3; re-confirmed once more on the thirtieth-wave full re-score, 2026-08-31, with every maturity value holding; re-confirmed again on the thirty-first-wave full re-score, 2026-09-01, at v1.179.0, which refuted §17 3→4 and §30 3→4 (the seventh consecutive refusal) once more. The prior move was the twenty-first wave's §12 Maturity 3→4: 312 + 2 = 314.) - Implementation index = Σ(impl×weight) ÷ Σ(weight×10) = 690 ÷ 800 = 86.3% (thirty-second-wave full re-score, 2026-09-04, at v1.185.0: two moves on this axis in opposite directions, §31 Cost Efficiency / FinOps Implementation 7→8 on weight 2 (+2, on the probe-telemetry filter shipped in v1.182.0) and §7 Microservices Readiness Implementation 9→8 on weight 3 (−3, on the rubric-v2 Anti-Corruption-Layer / Strangler-Fig naming criterion), so 691 + 2 − 3 = 690; four further 8→9 lifts (§12, §13, §17, §30) were refuted on the adversarial pass and held at prior, and §10 was scored fresh under its v2 name at the I9 it was already carrying. Prior figure 691 ÷ 800 = 86.4% after the rubric v2 rebase of the same date: §10 carried at I9 but re-weighted 2→3 (+9), §16 N/A removes 8×2 (−16), Σweight 81→80, no score moved; prior figure 698 ÷ 810 = 86.2%, held on the thirty-first-wave re-score, 2026-09-01, at v1.179.0: eight proposals refuted on adversarial re-verification and held at prior, seven 8→9 (§4, §5, §11, §13, §17, §30, §34) and one 9→10 (§8). Up from 84.8% (687/810) on the thirtieth-wave re-score, 2026-08-31: §8, §19 and §26 each Implementation 8→9 on weight 3, §25 8→9 on weight 2, 687 + 11 = 698, all four on work shipped in commit
59d7a97/PR #325 at v1.175.0, landed after the same-day twenty-ninth-wave adjudication of the prior tree; three further 8→9 proposals (§11, §13, §23) were refuted on adversarial re-verification and held at prior. The index had held at 84.8% across the twenty-fifth through twenty-ninth waves, whose adversarial passes refuted twenty-nine lift proposals in aggregate (the twenty-ninth alone refuted eleven); the previous move was the twenty-fifth wave's §10 Implementation 8→9: 685 + 2 = 687.) - Recalibration (2026-08-01): Implementation 10 is awardable for an almost perfect implementation (every criterion at reference quality, no red flags, at most trivial polish left), so the former "attainable ceiling" reporting line is retired and the index reads directly against 100%. The denominators stay ×4 and ×10, so the raw percentages remain comparable to every prior cycle; the backlog's scheduling target stays 9 (the 9→10 rung is recognition at re-score time, not scheduled work), and Σ implPriority = 30 across 13 categories is the actual scheduled gap ranked in the backlog's implementation band (down from 42 when #8, #19, #25 and #26 left the band on the thirtieth wave; the figure read 31 while the now-retired §16 row still counted, and 29 across twelve live rows going into this cycle; the thirty-second wave adds #7 at implPriority 3 on its 9→8 move and drops #31 from 4 to 2 on its 7→8 lift).
- Weaker axis: Implementation (execution quality), by ~11.2 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 80). §27 Internationalization is scored (ADR-027 superseded the single-locale ADR-011), so 33 categories count
- §32 weight = 3 (raised from the default 2 because MMCA.Common is a published framework)
Top 5 strengths
Dual-enforced Clean Architecture dependency rule (compile-time + fitness functions), §3 (impl 9):
Source/Build/MMCA.Common.LayerEnforcement.targets:1-90fails the build on layer violation (BeforeTargets ResolveProjectReferences);LayerDependencyTests.cs:12-58andDomainPurityTests.cs:13-26re-enforce Shared→Domain→Application→Infra→API/UI and Domain framework-freedom via NetArchTest. Reinforced by §34 (impl 8).- Remediation: Preserve by keeping both gates wired into CI and adding a regression test whenever a new layer/package is introduced; document the targets file in CLAUDE.md. To push the residual point, narrow the broader base-controller/UoW surfaces noted in §1.
- Expected delta: Preserves +9 impl on §3/§34; protects §16 maintainability from regression.
Reference-quality test strategy (define-once fitness functions + an enforced coverage gate) §14 (impl 9): right-shaped pyramid (1880
[Fact]/[Theory]across 262 files) with executable architecture fitness functions (LayerDependency, DomainPurity, MicroserviceExtraction, DependencyVersion, PiiConvention, PiiErasureContract, AggregateConvention, EventVersioningConvention, FrameworkSanity, Specification, SliceCohesion) authored once in the shippedMMCA.Common.Testing.Architecturepackage (122 methods across 46 abstract bases) and re-run identically across all three repos (ADR-015), a zero-discovery min-tests guard, a shipped reusable E2E package, and a 68.3% line-coverage floor that fails the build (ci.yml:226, ratcheted from 53% by the 2026-07 coverage program, measured ~70.3%).- Remediation: Preserve by keeping the floor green and ratcheting it upward over releases; add mutation testing on the Core tier to reach a 10.
- Expected delta: Holds §14 at impl 9; mutation testing on the Core tier is the remaining path toward 10; protects §28.
Supply-chain hardening with a license-pin build gate, a blocking SBOM, and Dependabot held-package guards, §32 (impl 9, weight 3):
Directory.Build.props:8-10(lock files, NuGetAudit, audit=all);Directory.Packages.props:54-56(MassTransit 8.5.5 pinned with documented v9 commercial-license crash hazard, ADR-016);DependencyVersionTestsBase.cs:24-37fails the build if any MassTransit package goes major≥9; 24packages.lock.json; CycloneDX SBOM a hard release gate (release.yml:45-50); and a committed.github/dependabot.yml:24-35withsemver-majorignores on every held package, the pin discipline now lives in config as well as the test.- Remediation: Automate the coordinated consumer version sweep (currently policy) to close the one remaining minor gap.
- Expected delta: Automating the sweep nudges §32 maturity toward fully-exemplary; preserves +9 impl.
Centralized, alg-pinned security with shipped anti-automation defenses and PBKDF2 hashing, §11 (impl 8):
WebApplicationBuilderExtensions.cs:364,380pins ValidAlgorithms per RS256/HS256 (defeats alg-confusion) with full issuer/audience/lifetime/signing-key validation,:217extends the RS256 pin to the JWKS-forwarded path;:34rate-limit bypass for health/.well-known/grpc;LoginProtectionService.cs:50-56,74-82adds exponential brute-force lockout + per-IP registration limit (ADR-029);PasswordHasher.cs:9,53hashes with PBKDF2-HMAC-SHA512 (600k iterations, PBKDF2-only: the legacy-salt verify+migrate branch has been removed,FixedTimeEquals, ADR-032): all returningResult.Failurerather than throwing.- Remediation: Keep the CI vulnerable-package gate green; document the deployer's vault/managed-identity binding (the acknowledged gap holding impl at 8) with a reference wiring sample.
- Expected delta: A documented MI binding sample nudges §11 toward 9 and supports §17/§26.
Architecture-test-enforced microservices extraction boundaries + event versioning, §7 (maturity 4, impl 8): ADR-006 (database-per-service), transport-agnostic
IMessageBus.cs:28, and the sharedRules/Layering/ArchitectureRules.Transport.cs:19rule (subclassed throughBases/Layering/MicroserviceExtractionTestsBase.cs:13inMicroserviceExtractionTests.cs:11, run atci.yml:144under a required check) forbidding Application/Domain/Shared→MassTransit and Grpc→Domain/Application/Infrastructure. Reinforced by §6'sBaseIntegrationEvent.SchemaVersion(ADR-010) +EventVersioningConventionTests, so cross-service contracts carry an explicit version, and by ADR-090 upcasting for retired contracts. The strength is the enforcement, which is why maturity holds at 4; implementation stepped back to 8 on the thirty-second wave because rubric v2 now asks for the modernization patterns to be named, and neither Anti-Corruption Layer nor Strangler Fig appears anywhere in the repo (ArchitectureEvaluationCriteria.md:263).- Remediation: Name the Anti-Corruption Layer over the gRPC adapter convention (
Grpc/DependencyInjection.cs:58) and record the Strangler Fig route (new path beside old, traffic moved, old path retired) in ADR-007/008; separately, demonstrate one end-to-end lift-out (the Notifications module) in a sample to convert 'extractable by design' into 'extracted and proven'. - Expected delta: +1 impl on §7 (8→9, +3 weighted) once both patterns are named; the enforcement half is already preserved at maturity 4.
- Remediation: Name the Anti-Corruption Layer over the gRPC adapter convention (
Top 5 risks
Note (twenty-first wave, v1.121.0): earlier waves closed risks previously listed here (§29's restore drill, the §27 i18n train, §24 forms enforcement, §22's firefox gate, §23's WebVitals gate, §25's navigation drift gate), and this wave closes another: §12's perf gate is now a required check on
main, so the category returns to the protect list and its risk entry is retired. A documentation-drift risk entered in its place and was closed the same day:CONTRIBUTING.mdstill advertised the pre-promotion gate list, and because scorecard adjudications cite that file, its staleness mis-grounded §12's own score for a full cycle (fixed in MMCA.Common PR #100,658786b; kept in the list with its resolution for provenance). The remaining deeper execution each risk names (per-service cost attribution, CD-to-Azure, a CI migration-apply step, consumer-side privacy governance) still lives downstream. Note (2026-08-22): risk 2 (§9's missing in-repo contract gate) closed on MMCA.Common PR #271 and is kept in place with its resolution, so the surviving entries are not renumbered.
FinOps execution and governance live downstream: §31 (impl 8, maturity 2, the lowest maturity on the board; a documented accepted cap per the backlog's Deliberate / accepted section): the framework ships real cost levers (an OTel
Telemetry:TracesSampleRatiosampler knob, the outbox per-message log trimmed to Debug,OutboxPollFilterProcessor, and since v1.182.0 the default-ON probe-telemetry filter plus its span processor,Aspire/Extensions.cs:224,248, that keep health-probe traces and their SQL/Redis children out of ingestion), but right-sizing, reversible scale-events, and per-service cost attribution are consumer/IaC concerns a library cannot perform, and no in-repo process or review enforces cost discipline, so maturity stays at 2 while implementation rose to 8 on the thirty-second wave.- Remediation: The in-repo telemetry-cost half is done and now scores 8. What remains is structural: the consumer apps must demonstrate measured right-sizing and per-service cost attribution that the framework's knobs feed, and a cost convention would have to be enforced somewhere (nothing under
Tests/Architectureor in any workflow gates cost today). - Expected delta: The +1 impl (7→8, +2 weighted) landed this cycle; +1 maturity on §31 (2→3) still needs an enforced cost convention, and 8→9 needs the consumer-side right-sizing and attribution evidence.
- Remediation: The in-repo telemetry-cost half is done and now scores 8. What remains is structural: the consumer apps must demonstrate measured right-sizing and per-service cost attribution that the framework's knobs feed, and a cost convention would have to be enforced somewhere (nothing under
API contract stability has no in-repo automatic enforcementRESOLVED 2026-08-22 (MMCA.Common PR #271, squash8a6c603): both halves the entry prescribed shipped in-repo, so §9 rises to Maturity 4 and returns to the protect list. The OpenAPI drift gate now exists at the framework level (OpenApiBaselineTests.cs:45-77diffs the normalized generated document against the committedopenapi-baseline.v1.json, regenerated only throughMMCA_UPDATE_OPENAPI_BASELINE=1in the same pull request) while each consumer host keeps guarding its own concrete surface, and the previously-absent[ServiceContract]rule ships as a dedicated fitness function (ArchitectureRules.Contracts.cs:32+Bases/ServiceContractPurityTestsBase.cs:24-26, subclassed atServiceContractPurityTests.cs:11). Recorded here with its resolution for provenance, the same way risk 5 is; the entry as raised read: §9 (impl 9, maturity 3, weight 2) has genuinely strong contract substance (centralized RFC 9457 ProblemDetails mapping, header-based versioning with a backward-compatible v1.0 default, the ADR-034 generic query contract, OpenAPI + Scalar UI helpers), but no §9-specific fitness function or CI job exists in-repo, so contract discipline is enforced by review and convention, the rubric's Maturity-3 band. (§25, which this entry displaced, closed on theNavigationContractTestsdrift gate.)- Remediation: Done, via the first of the two levers the entry named (a minimal in-repo contract-surface fitness check over the framework-owned pieces) rather than the documented-acceptance alternative. Preserve by regenerating the baseline deliberately in the pull request that changes the contract, and by subclassing
ServiceContractPurityTestsBasein each consumer so the attribute-driven rule runs where contract types actually live. - Expected delta: +2 weighted maturity on §9 (6/18 → 8/18), lifting the Maturity index 314 → 316/324 (96.9% → 97.5%); Implementation holds at 9 (the baseline covers the framework-owned surface only and the purity rule's in-repo run stays a ratchet until a type carries the attribute).
- Remediation: Done, via the first of the two levers the entry named (a minimal in-repo contract-surface fitness check over the framework-owned pieces) rather than the documented-acceptance alternative. Preserve by regenerating the baseline deliberately in the pull request that changes the contract, and by subclassing
Deployment gating verified only downstream (§17 DevOps, impl 8: a reference
samples/deploymentBicep/OIDC sample +DEPLOYMENT.mdship and are compile-checked per pull request, though that job is not a required context, and a library cannot self-deploy anyway, so real CD-to-Azure lives in consumers; the v1.128.0 keyless nuget.org publishing path under ADR-053 hardens the release identity but not the deploy axis).and §8 Data (no verifiable CI migration-apply step here)the §8 half RESOLVED 2026-08-31 (commit59d7a97/PR #325, kept here with its resolution for provenance): the ephemeral-DB migration-apply step this entry's remediation named now exists as a required-gate CI job with outcome assertions (ci.yml:578,597) plus an in-repo DB-free apply proof (MigrationApplyProofTests.cs:92), so §8 rises to Implementation 9 and leaves the backlog's implementation band. §17's mechanism-shipped-execution-downstream shape remains, now the sole member of that group.- Remediation: Done for §8, via exactly the CI apply step the entry prescribed. For §17: a smoke-deploy of the
samples/deploymentsample (raises §17 toward proven-not-just-referenced) and fixing the danglingsecretRef: 'sql-conn'binding (backlog #17). - Expected delta: The §8 +1 impl landed this cycle (part of 687 → 698); +1 impl on §17 remains available downstream.
- Remediation: Done for §8, via exactly the CI apply step the entry prescribed. For §17: a smoke-deploy of the
Compliance governance is consumer-owned; the in-repo guard verifies a mechanism, not the category, §30 (impl 8, maturity 3, weight 2): the headline §30 red flags are closed in-framework (the
IAnonymizableerasure extension point, AES-256-GCM at rest, and the shipped+testedPiiRedactormasking[Pii]members before logs/telemetry), andPiiErasureContractFitnessTestsnow forces a[Pii]type throughPiiRedactor+IAnonymizableso the erasure contract is no longer un-gated. But that gate verifies the mechanism rather than gating a repo-wide convention (the structuralPiiConventionTestsscan is still vacuous (no PII-bearing type lives in Common's Domain), and the governing process) the personal-data inventory, DSAR/export endpoints, consent capture, residency verification, retention execution: is delegated to consumers. Maturity held at 3 (reviewed this cycle): the category as a whole is not automatically enforced.- Remediation: The in-repo mechanism is done; further maturity movement needs the consumer apps to carry the personal-data inventory + DSAR/consent/residency process the framework's
[Pii]/IAnonymizableextension points feed (documented in ADR-005). - Expected delta: +1 maturity on §30 (3→4) is a consumer-side governance lift, not an in-repo one; the framework half is complete.
- Remediation: The in-repo mechanism is done; further maturity movement needs the consumer apps to carry the personal-data inventory + DSAR/consent/residency process the framework's
The documented merge-gate list has drifted from live branch protection, and adjudications cite itRESOLVED 2026-07-21, same day it was raised (MMCA.Common PR #100,658786b): the gate list and the reproduce snippet now name all eight live contexts, verified byte-identical against the protection API, and the file now directs readers to that API as authoritative over the committed copy. Recorded here for provenance; the finding as raised read: (§34 / §16, both impl 8)CONTRIBUTING.md:57-64lists five required gates and calls webkit advisory, and its reproduce snippet (CONTRIBUTING.md:104-112,124) omits webkit, the Helpdesk consumer-source canary, and the perf gate, while liverequired_status_checksonmaincarries all eight contexts (branch-protection API, this run;ci.yml:116-118already asserts all three browser engines are required). This is the rubric's "stale docs" red flag with a governance edge: the twentieth wave held §12 at Maturity 3 partly on this file's authority, and the score was only corrected a cycle later.- Remediation: Done. The gate list and reproduce snippet were synced and the API is now named as authoritative; the backlog item under #34 is closed with the proving reference.
- Expected delta: no index move, as expected; it protects future scores from being mis-grounded and removes a §16/§34 stale-docs deduction. §34 holds M4/I8 this cycle; the next re-score can weigh the fix on fresh evidence.
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).