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

Architecture governance

MMCA.ADC: Architecture Remediation Backlog

Derived from ArchitectureScorecard.md (evidence as of 2026-10-18 at MMCA.Common.* v1.244.0): Maturity 96.9% (314/324) / Implementation 86.0% (697/810), weight total 81, no N/A category. Work is ranked on both scorecard axes, one band per axis. Maturity band: every category scoring maturity < 4, ranked by (4 - maturity) x weight. Implementation band: every category scoring implementation <= 8, ranked by max(0, 9 - implementation) x weight (target 9: a 10 is awardable at re-score time for an almost perfect implementation, but ranking against 10 would drown the real gaps, so 9 governs scheduling). Ties break by weight descending, then category number. Today 4 categories sit in the maturity band (10 weighted points), 14 in the implementation band (34 gap points), 30 score maturity 4 (protect, do not regress) and 19 of those also score implementation >= 9, which is the only combination that is done on both axes. This is the single remediation and tech-debt ledger: each deferred sub-item keeps its TD-NN ID with its blocker, resolution path and effort. Effort key: S = hours · M = about 1 day · L = multi-day. Right-size every fix to the measured conference load (~76 accounts / ~67 peak concurrent).

Open work at a glance

Triage 2026-10-19. The A2 wave (ADC #280, MMCA.Common v1.245.0) shipped the §10, §15, §22, §23, §25 and §31 levers and adopted the §9 detector; each is marked shipped below, with its score movement pending the next re-score. Every other open item was weighed for over-engineering at the measured load: the ones closed are under Deliberate and accepted (Closed as over-engineering, 2026-10-19) and must not be re-proposed without new evidence. Scores are unchanged, so every row keeps its band position. The only open work is the manual screen-reader pass (§21), which needs a human.

Priority # Category M / I Band Next action
4 §22 Responsive & Cross-Browser 3 / 7 Maturity and implementation Phone-viewport E2E shipped (ADC #280); density and table reflow closed 2026-10-19; re-score pending
3 §7 Microservices Readiness 4 / 8 Implementation Closed 2026-10-19 (no lever; the cached bookmark-count read is accepted)
3 §10 Messaging & Integration Architecture 3 / 8 Maturity and implementation Error-queue alert-as-code and runbook shipped (ADC #280); re-score pending
3 §11 Security 4 / 8 Implementation Closed 2026-10-19 (the threat model is deliberately not scheduled)
3 §18 UI Architecture & Components 4 / 8 Implementation Closed 2026-10-19: TD-16 is not scheduled; the 400-line cap forces a split when one is due
3 §21 Accessibility (a11y) 3 / 8 Maturity and implementation Open (needs a human): record the dated manual screen-reader pass
2 §5 Vertical Slice Architecture 4 / 8 Implementation Closed 2026-10-19 (the layered-by-project hybrid is the accepted cap)
2 §9 API & Contract Design 4 / 8 Implementation Parameter-aware detector adopted at v1.245.0 (Common #567); the baseline ratchet is cut; re-score pending
2 §12 Performance & Scalability 3 / 9 Maturity Held by decision: the in-band capacity proof is declined (see Deliberate and accepted)
2 §15 Best Practices & Code Quality 4 / 8 Implementation TD-18 shipped: weekly MAUI head build (ADC #280); re-score pending
2 §23 Front-End Performance 4 / 8 Implementation Image recompressed and EFIGS ICU pinned (ADC #280); re-score pending
2 §24 Forms, Validation & UX Safety 4 / 8 Implementation Closed 2026-10-19: the 21-form accessibility tests would mirror one shared component
2 §25 Navigation & Information Arch 4 / 8 Implementation Nav-item-to-page-guard test shipped (ADC #280); re-score pending
2 §31 Cost Efficiency / FinOps 4 / 8 Implementation cost-guard runs daily (ADC #280); re-score pending
1 §27 Internationalization (i18n) 4 / 8 Implementation Deferred by decision (see Deliberate and accepted)
- §28 Front-End Testing & Quality 4 / 9 Outside both bands Closed 2026-10-19: pixel-level visual regression is not adopted
- §26 Front-End Security 4 / 9 Outside both bands TD-08 resolved: the same-origin proxy is adopted (Common backlog, Resolved)

Open items

[x] #10 · Messaging & Integration Architecture · M3 / I8 (weight 3; maturity priority 3, implPriority 3)

Shipped 2026-10-19 (ADC #280): the adc-prod-alert-broker-error-queue rule matches "moved to the error queue" beside the outbox dead-letter rule (infra/main.bicep:604), and infra/OPERATIONS.md:232 holds the error-queue runbook (queue names, inspect, replay, discard); ADR-087 records it (Website #260). Score movement is pending the next re-score. The original finding follows. Most criteria are CI-enforced (a frozen contract snapshot that spells out generic arguments and is keyed by the [EventName] wire identity, Common ArchitectureRules.Events.cs:45,:82-89; every integration event must declare that name, Common Bases/Contracts/EventConventionTestsBase.cs:21 inherited by Contracts/EventConventionTests.cs:3; gateway route-map tests in MMCA.ADC.CI.slnf:60; the Service Bus emulator tier gating deploys through cross-service-freshness, cross-service-tests.yml:142 and deploy.yml:884, documented as authoritative in AGENTS.md:76), but broker faults are not wired to alert-as-code, which is the maturity 3→4 lever. M4 is also defensible; M3 is the adjudicated score. The missing ordering stamp on the speaker-link handlers is accepted (see Deliberate and accepted).

  • Dead-letter procedure (implementation 8→9, and the maturity lever when wired as alert-as-code). Add an alert on "moved to the error queue" in infra/main.bicep beside the outbox dead-letter rule (:603-605) and an infra/OPERATIONS.md error-queue section (inspect, replay, discard). FaultIntegrationEventConsumer.cs:57 (Common) logs "faulted ... moved to the error queue", but the only messaging log alert matches "dead-lettered", the outbox text (infra/main.bicep:605); the dead-letter runbook covers outbox rows only (infra/OPERATIONS.md:188, manual replay by a row reset at :219), and ADR-087 records no alert, runbook or replay path (../adr/087-broker-poison-message-handling.md:202-204). Rubric criterion: ArchitectureEvaluationCriteria.md:336. Effort S-M.

[ ] #21 · Accessibility · M3 / I8 (weight 3; maturity priority 3, implPriority 3)

The axe layer is an enforced deploy gate (45 scans, AccessibilityTests.cs:23, inside the chromium e2e-gate), with a stated WCAG 2.1 AA target, and the live-moderation dialog carries a keyboard-operability E2E test (Tests/E2E/MMCA.ADC.E2E.Tests/Workflows/Engagement/LiveModerationKeyboardTests.cs:23, a subclass of Common's KeyboardOperabilityTestsBase). Maturity stops at 3 because the rubric pairs automated CI checks with a recorded manual screen-reader pass. The implementation lever is not yet identified (axe is E2E-only, chromium-only, non-PR and conditional, and keyboard coverage is one dialog).

  • Record a dated manual screen-reader pass in adc-ACCESSIBILITY-SCREENREADER-PASS.md (Website docs-src/guides/), whose results table holds only the empty placeholder row (:64). Needs a human with NVDA or VoiceOver against the running Aspire app; it cannot be done headless. This is the cheapest maturity 3→4 win.

[~] #22 · Responsive & Cross-Browser · M3 / I7 (weight 2; maturity priority 2, implPriority 4, the top of the implementation band)

Cross-engine recency is gated by the cross-browser-freshness step of the freshness job (deploy.yml:923, :1159, :1193), the only automated check in the category; it does not count toward implementation.

  • Automated phone-viewport check (maturity 3→4). Shipped 2026-10-19 (ADC #280): Tests/E2E/MMCA.ADC.E2E.Tests/Workflows/Conference/Public/PhoneViewportTests.cs sets 390x844 and asserts a visible heading and no horizontal overflow on the public home, session list and detail, and speaker list and detail, inside the chromium e2e-gate. Re-score pending. The original finding follows. No ADC test sets a phone viewport (the only phone-viewport E2E is Common's UserPreferencesTestsBase.cs:92 at 390x844), no responsive fitness test exists in Tests/Architecture, and bUnit asserts the mobile branch on some list pages only (UserListTests.cs:331), so reflow, density and touch targets are review-enforced. Add an E2E or fitness check that asserts layout at a phone breakpoint. Effort M.
  • [closed] Density options (closed 2026-10-19, see Deliberate and accepted). The framework ships the capability (DataGridListPageBase.cs:78 DenseGrid, :909 ToggleDensity()) and ADC adopts none of it; EventList.razor:73 hardcodes Dense="true".
  • [closed] Content reflow on the raw-table surfaces (closed 2026-10-19, see Deliberate and accepted). 20 .razor files render MudSimpleTable/<table> outside the DataGrid; 12 are key/value detail tables that wrap on mobile (adc-ui.css:21-40), while the session-selection statistics tables use fixed-width columns with no mobile alternative (adc-ui.css:80), alongside the organizer surfaces (OrganizerPointsOverview.razor:61,:98, OrganizerAttendance.razor:50); the DataGrid pages already degrade to card lists. Effort M together with density.

[closed] #18 · UI Architecture & Components · M4 / I8 (weight 3; implPriority 3)

  • [closed] TD-16 (the §18 impl 8→9 lever, effort S; closed 2026-10-19, see Deliberate and accepted). The enforced cap is MaxCodeBehindLines => 400 (MMCA.Common/Source/Hosting/MMCA.Common.Testing.Architecture/Bases/Ui/UIArchitectureConventionTestsBase.cs:22, unoverridden in Ui/UIArchitectureConventionTests.cs:10-13). Two pages are extracted: PublicSessionList.razor.cs (its event catalog in PublicSessionEventCatalog.cs, page at 328, ADC #264) and SessionFeedback.razor.cs (its answer state in Engagement.UI/Pages/Feedback/SessionFeedbackAnswers.cs, page at 299, ADC #265). Measured with File.ReadAllLines (as the cap counts, UIArchitectureConventionTestsBase.cs:52), the high-water code-behind is now Conference.UI/Pages/Home/ADCHome.razor.cs at 370 (30 lines of headroom), then EventDetail 354, EventFeedback 351, SessionAssetsPanel 349, SessionLive 347, SpeakerDashboard 340, SpeakerDetail 339, PublicSessionDetail 334. Extract from ADCHome next. §18 holds at 8 because extraction is applied file by file rather than systematically, and the component-lifetime (render hooks) and render-fragment/slot criteria are thinly met. Blocker: none. Resolution path: presentational sub-component extraction per the TD-13 pattern, markup moved verbatim so the rendered DOM and the E2E selectors are unchanged. The cap itself stays at 400 (see Deliberate and accepted).

[x] #9 · API & Contract Design · M4 / I8 (weight 2; implPriority 2)

Shipped 2026-10-19: the Common detector compares operation parameters and request bodies (Common #567, OpenApiBreakingChangeDetector.cs:271), adopted at v1.245.0 by ADC #280. The baseline ratchet is cut (see Deliberate and accepted). Re-score pending. The original finding follows. The baseline-diff half of the contract-test criterion ships: all four services diff the live document against a committed Contract/openapi-baseline.json (Conference Tests/Integration/MMCA.ADC.Conference.IntegrationTests/Contract/OpenApiContractTests.cs:29, Identity :39, Engagement :30, Notification :31) through the Common base's opt-in mode (OpenApiContractTestsBase.cs:21-28, failing on any detected breaking change at :118-159), inside the required integration-tests check (deploy.yml:641-729). The AsyncAPI half is accepted, not scheduled (see Deliberate and accepted), so TD-24 is closed (see Resolved).

  • Parameter-aware, ratcheting breaking-change detection (implementation lever; Common first; parameter half shipped, ratchet cut). CompareOperation reads only request bodies and responses, never operation parameters (MMCA.Common/Source/Hosting/MMCA.Common.Testing/Conformance/OpenApiBreakingChangeDetector.cs:190-216; the Conference baseline carries 159 parameters blocks), so a removed, renamed or newly required query or path parameter, or a parameter type change, passes; a request body is compared only when both sides have one (:192-193) and a removed media type passes (:226-230). The baseline does not ratchet: nothing keeps openapi-baseline.json in step with the committed build documents (Source/Services/*/openapi/*.json, drift-gated at deploy.yml:305-320), so a route added after seeding is unprotected until the baseline is refreshed by hand. Blocker: the detector lives in MMCA.Common, so parameter comparison is a framework change first. Resolution path: extend the detector to operation parameters and request-body presence, then keep each ADC baseline in step with its committed build document. With the async half accepted, the frozen event and proto snapshots carry the async side of the criterion; whether they read as its equivalent decides a 9 at the next re-score.

[x] #15 · Best Practices & Code Quality · M4 / I8 (weight 2; implPriority 2)

  • TD-18 (effort L) · the MAUI app is outside every CI build. Shipped 2026-10-19 (ADC #280) as the reduced lever: the weekly maui-audit.yml now builds MMCA.ADC.UI for net10.0-android in Release with analyzers and TreatWarningsAsErrors (.github/workflows/maui-audit.yml:197-198). Detection only, no per-PR leg. Re-score pending. The original finding follows. MMCA.ADC.CI.slnf lists only UI.Web (:25) and UI.Web.Client (:26); the weekly maui-audit.yml installs the maui-android workload (:109) only to restore and audit (:93, :128), so MMCA.ADC.UI is never compiled in CI and its analyzers, TreatWarningsAsErrors and its own NoWarn CA5392 (Source/Hosts/UI/MMCA.ADC.UI/MMCA.ADC.UI.csproj:155) are review-enforced only. The gating vulnerable-package audit is the shared MMCA.Common composite action in the supply-chain job (deploy.yml:577, uses at :608), scoped to MMCA.ADC.CI.slnf at :610. .github/workflows/maui-audit.yml audits the MAUI graph weekly (android-only, in no deploy.needs), so the detector half exists and the gating half does not. Blocker: a MAUI leg installs the maui-android workload on every run, against the deliberate Actions-minute budget. Resolution path: a scheduled (not per-PR) MAUI build leg, or wiring the audit result somewhere that can fail, verified by a full-solution package-mode restore rather than CI.slnf; Store carries the identical gap and should be swept in the same pass. Maturity 4 on §15 is CI.slnf-wide, not repo-wide, while this is open.

[x] #23 · Front-End Performance · M4 / I8 (weight 2; implPriority 2)

  • Optimize images and load the EFIGS ICU shard. Shipped 2026-10-19 (ADC #280): jared-rhodes.jpg re-encoded from 313,143 to 30,397 bytes, and the client pins BlazorIcuDataFileName to icudt_EFIGS.dat (MMCA.ADC.UI.Web.Client.csproj:16); Brotli _framework payload 5405 KB to 5224 KB. Re-score pending. The original finding follows. The WebAssembly head loads all globalization data (BlazorWebAssemblyLoadAllGlobalizationData at Source/Hosts/UI/MMCA.ADC.UI.Web.Client/MMCA.ADC.UI.Web.Client.csproj:11) for an en-US + es app; record the Brotli payload before and after. The CWV budgets themselves are enforced (WebVitalsTests.cs:39), and a 5900 KB Brotli payload budget runs on every code PR (wasm-payload-budget, deploy.yml:476, budget-kb at :497) without being a required check (:472), so promoting it to a required check is a candidate cheap lever. Code-splitting the bundle is declined (see Deliberate and accepted).

[closed] #24 · Forms, Validation & UX Safety · M4 / I8 (weight 2; implPriority 2)

  • [closed] Extend ShouldExposeFieldErrorAccessibly to the remaining MudForm pages (implementation 8→9, effort S-M; closed 2026-10-19, see Deliberate and accepted). The render-level assertion of role=alert, aria-invalid and aria-describedby on a field error covers EventCreate only (Tests/Modules/Conference/MMCA.ADC.Conference.UI.Tests/Pages/Events/EventCreateTests.cs:104), against 21 MudForm files that each render an <ErrorSummary> (EventCreate.razor:41). The forms fitness gate (Ui/FormsConventionTests.cs:17, MinimumCreateForms => 9) is string-marker based and scans the create forms and Profile only.

[x] #25 · Navigation & Information Architecture · M4 / I8 (weight 2; implPriority 2)

  • ADC-side navigation-contract test. Tests/Architecture/MMCA.ADC.Architecture.Tests/Api/NavigationContractTests.cs compares every @page route and its guard with the Route Contract table in adc-NavigationFlow.md:639 (set equality :63-76, access :79-104, missing doc fails :246); build-and-test downloads the published doc (deploy.yml:338-348). A route or guard change now needs its Website doc PR merged first.
  • Type the bare {Id} routes. All 13 are typed: :int for the integer-keyed aggregates and :guid for the two speaker routes, with typed parameters in place of the Parse<T> coercion (EventDetail.razor:1 with EventDetail.razor.cs:27, SpeakerDetail.razor.cs:34, PublicSpeakerDetail.razor.cs:31, SessionFeedback.razor.cs:24), so a malformed id answers with the router's not-found page. /roles/{Role} stays untyped (a role name). The Route Contract rows match (adc-NavigationFlow.md:639), enforced by NavigationContractTests.
  • Guard the speaker self-edit form. SpeakerDashboard.razor:11 carries UnsavedChangesGuard, dirty only while an edited field differs from the loaded profile (SpeakerDashboard.razor.cs:69); pinned by SpeakerDashboardTests.cs:287.
  • /profile/sessions hidden but routable: decided, recorded under Deliberate and accepted.
  • Nav-item-to-page-guard fitness test (effort S). Shipped 2026-10-19 (ADC #280): Tests/Architecture/MMCA.ADC.Architecture.Tests/Api/NavItemGuardContractTests.cs fails when any nav item is looser than its page; it found and fixed two (Happening Now moved to the signed-in section, Roles gated on Organizer). Re-score pending. The original finding: Each module's IUIModule.NavItems declares a RequiredRole / RequiredClaim / RequiredPermission (EngagementUIModule.cs:25-28), but no test pairs it with the guard of the page it opens; NavigationContractTests.cs:44-104 covers routes and page guards only. A test that resolves each nav item's route to its @page and asserts the menu requirement is no looser than the page guard closes the review-enforced half of the role-based-flows criterion.
  • URL-projected list state: already shipped, so this residual was a misread. Every list page on DataGridListPageBase mirrors page, page size, sort and its named filters into the query string on each change (Common DataGridListPageBase.cs:978-981 through ListPageQueryStateService.ReplaceState, ListPageQueryStateService.cs:203-216) and reads the URL first on load (DataGridListPageBase.cs:217-228), so the public session, event and speaker lists carry shareable state; ?mine is an additional named parameter, not the only one. The activity and sponsor lists have no adjustable state.

[x] #31 · Cost Efficiency / FinOps · M4 / I8 (weight 2; implPriority 2)

  • Run cost-guard daily (effort S), shipped 2026-10-19 (ADC #280: cost-guard.yml:14, cron 0 7 * * *; still read-only, no auto-revert; re-score pending). The original finding: the open half of the rubric's "reversible scale events" criterion. The surge is declared in IaC (conferenceMode, infra/main.bicep:157, sizing :191-192, SQL tier :995-997) and switched by the CONFERENCE_MODE_UNTIL repository variable on every deploy (deploy.yml:1430-1445), so the first deploy after the window is the revert (deploy.yml:793), and cost-guard fails an expired-but-live surge (cost-guard.yml:116-118). The surge is reverted: az containerapp list -g acc-rg reads all six adc-prod-* apps at minReplicas 1 / maxReplicas 2, and CONFERENCE_MODE_UNTIL is no longer a repository variable. The structural gap stands: nothing schedules the revert deploy, and the read-only guard runs weekly (cost-guard.yml:14, Mondays 07:00 UTC), so an expired surge can bill for up to a week before it is flagged. Distinct from TD-15, the topology-collapse option under Deliberate and accepted.

Rows with no recorded lever (closed 2026-10-19)

§5 (the accepted layered-by-project hybrid is the cap and is not itself a lever), §7 (eight typed gRPC client registrations with two bidirectional pairs and a three-node call loop; the modernization-patterns criterion is met in the ADRs, ../adr/007-grpc-extraction.md:26 and ../adr/008-service-extraction-topology.md:52, and the cached bookmark-count read is accepted) and §11 (the committed threat model is deliberately not scheduled), plus §21's implementation half. Closed on 2026-10-19: each would need new machinery (a broker projection, a threat model, a slice re-layout, axe on every PR) against no observed defect. A re-score records them as the cap and does not invent a lever.

Outside both bands (the 9→10 rung, not ranked)

[x] #26 · Front-End Security · M4 / I9

  • TD-08 (effort L) · same-origin data-call proxy. Resolved: the Common backlog records TD-08 under Resolved (SameOriginApiProxyEndpoint.cs:29), adopted here at Source/Hosts/UI/MMCA.ADC.UI.Web/Program.cs:161,284.

[closed] #28 · Front-End Testing & Quality · M4 / I9

  • [closed] Pixel-level visual regression (closed 2026-10-19, see Deliberate and accepted). The markup-snapshot half exists (ComponentsSnapshotTests.cs:27, 12 [Fact]s over 12 committed .html baselines, missing baseline = non-match, in MMCA.ADC.CI.slnf:43,:49,:55); pixel-level Playwright visual regression is open, and 12 components is a small slice of the surface.

Implementation band

Categories scoring implementation <= 8, ranked by implPriority (target 9). §10, §21 and §22 also hold a maturity-band row; their items are under Open items. The rows and the levers below are as scored on 2026-10-18. After the 2026-10-19 triage, the §9, §10, §15, §22 (phone viewport), §23, §25 and §31 levers have shipped (ADC #280, re-score pending), and the §5, §7, §11, §18, §22 (density, reflow) and §24 levers are closed (Deliberate and accepted). Neither band carries other scheduled work.

implPriority # Category w Impl Recorded lever
4 §22 Responsive & Cross-Browser 2 7 Adopt the density-options criterion (zero adoption; DataGridListPageBase.cs:78 DenseGrid, :909 ToggleDensity(); EventList.razor:73 hardcodes Dense="true") and complete content reflow on the fixed-width selection-dashboard tables (adc-ui.css:80) and organizer surfaces (OrganizerPointsOverview.razor:61,:98, OrganizerAttendance.razor:50). Effort M
3 §7 Microservices Readiness 3 8 Not yet identified: eight typed gRPC client registrations (Conference.Service/Program.cs:392, Engagement.Service/Program.cs:281, Identity.Service/Program.cs:316, Notification.Service/Program.cs:225), two bidirectional pairs and a three-node call loop; the rubric v2 modernization-patterns criterion is met in the ADRs (Anti-Corruption Layer at ../adr/007-grpc-extraction.md:26, Strangler Fig at ../adr/008-service-extraction-topology.md:52); the cached Conference→Engagement bookmark-count read is accepted (Deliberate and accepted)
3 §10 Messaging & Integration Architecture 3 8 Alert, runbook and replay procedure for the broker error queue (FaultIntegrationEventConsumer.cs:57 vs the outbox-only rule at infra/main.bicep:603-605 and runbook at infra/OPERATIONS.md:188)
3 §11 Security 3 8 Not yet identified: no threat model (a committed model is deliberately not scheduled), service-to-service identity on four of seven gRPC surfaces (BookmarkCountsGrpcService, Engagement.Service/Program.cs:347, LiveChannelGrpcService, Notification.Service/Program.cs:293, and EventLiveValidationGrpcService, Conference.Service/ConferenceGrpcEndpoints.cs:32, are AllowAnonymous by network position), an FCM client key committed at MMCA.ADC.UI/appsettings.json:34 (deliberate, minor). The accepted SQL public-network exposure (infra/main.bicep:861, :867, :876) is the 9→10 rung
3 §18 UI Architecture & Components 3 8 TD-16: the high-water code-behind is ADCHome.razor.cs at 370 of the 400 cap (PublicSessionList and SessionFeedback extracted, ADC #264 and #265); systematic presentational sub-component extraction. Effort S
3 §21 Accessibility (a11y) 3 8 Not yet identified: axe is E2E-only, chromium-only, non-PR and conditional (45 scans, AccessibilityTests.cs:23), and keyboard operability is tested on one dialog (LiveModerationKeyboardTests.cs:23); the screen-reader pass is the maturity half
2 §5 Vertical Slice Architecture 2 8 Not yet identified: the accepted layered-by-project hybrid (DTOs in Shared with horizontal mapper/validator folders) is the recorded cap and is not itself a lever
2 §9 API & Contract Design 2 8 Parameter-aware, ratcheting breaking-change detection: the Common detector compares no operation parameters (OpenApiBreakingChangeDetector.cs:190-216) and the four committed baselines (OpenApiContractTests.cs:29) do not ratchet against the build documents; the AsyncAPI document is accepted as not built (Deliberate and accepted)
2 §15 Best Practices & Code Quality 2 8 TD-18: the MAUI head is in no CI build (MMCA.ADC.CI.slnf:25); maui-audit.yml runs weekly, android-only, in no deploy.needs, so the detector exists and the gate does not.
2 §23 Front-End Performance 2 8 Optimize images and load the EFIGS ICU shard (MMCA.ADC.UI.Web.Client.csproj:11 loads all globalization data); code-splitting is declined by decision (Deliberate and accepted)
2 §24 Forms, Validation & UX Safety 2 8 Extend ShouldExposeFieldErrorAccessibly (EventCreate only, EventCreateTests.cs:104) to the remaining MudForm pages; client/server parity is complete (BR-122 mirrored at SessionFormModel.cs:87-92, server error surfaced at SessionCreate.razor.cs:178) and the forms fitness gate is string-marker based. Effort S-M
2 §25 Navigation & Information Arch 2 8 The contract test ships (Api/NavigationContractTests.cs) and the four recorded residuals are closed (typed routes, the speaker form guard, /profile/sessions accepted, URL list state confirmed); 8 holds because nav-item requirements are not tested against page guards (EngagementUIModule.cs:25-28) and the flow diagrams sit outside the contract. Effort S
2 §31 Cost Efficiency / FinOps 2 8 Run the read-only cost-guard daily: the surge is IaC (infra/main.bicep:157) switched per deploy by CONFERENCE_MODE_UNTIL (deploy.yml:1430-1445) and is reverted, but nothing schedules a revert and the guard is weekly (cost-guard.yml:14, :116). Effort S
1 §27 Internationalization (i18n) 1 8 Deferred by decision: pseudo-localization covers 3 public pages (PseudoLocalizationTests.cs:41-43, public by design :25); do not re-propose without new evidence (re-open triggers under Deliberate and accepted)

Σ implPriority = 34 across the 14 live rows above (4+3+3+3+3+3+2+2+2+2+2+2+2+1); ties are broken by weight descending, then category number ascending. The index gap to a full 810 is 113 weighted points; the 34 are the scheduling gap to 9 on every category.

Maturity band

Priority # Category w Maturity Recorded lever
3 §10 Messaging & Integration Architecture 3 3 Wire broker faults to alert-as-code
3 §21 Accessibility (a11y) 3 3 Record the dated manual screen-reader pass (adc-ACCESSIBILITY-SCREENREADER-PASS.md:64)
2 §12 Performance & Scalability 2 3 Held by decision: an in-band capacity proof is declined, so the monthly k6 run behind the recency-only load-freshness gate (load-test.yml:23, deploy.yml:863) is the accepted posture (Deliberate and accepted)
2 §22 Responsive & Cross-Browser 2 3 An automated phone-viewport check (E2E or fitness test) so reflow, density and touch targets are enforced rather than review-only; the cross-browser-freshness recency gate (deploy.yml:923) covers only the browser matrix

Σ maturity priority = 10 across the 4 rows (3+3+2+2).

Deliberate and accepted

Recorded decisions, not scheduled work.

  • Closed as over-engineering, 2026-10-19. Each was weighed against the measured load (~76 accounts, ~67 peak) and one developer; none has a defect behind it that an existing gate misses, so each would add permanent machinery for a score point. Do not re-propose without new evidence (a named re-open trigger, an incident, or an explicit ask).
    • §22 DataGrid density toggle and organizer table reflow (adc-ui.css:80, OrganizerPointsOverview.razor:61,98, OrganizerAttendance.razor:50): no user asked for density, the organizer surfaces are used on desktop, and the phone-viewport E2E now guards the attendee pages. Re-open: that test fails, or an organizer reports a phone layout problem.
    • §18 TD-16 ADCHome.razor.cs extraction (370 of 400): under the cap, and the cap's fitness test forces the split the day it is due.
    • §24 ShouldExposeFieldErrorAccessibly on 21 more forms: the behavior lives in the one shared ErrorSummary, so 21 tests would mirror one component; EventCreate pins it.
    • §28 pixel-level visual regression: screenshot baselines break on every MudBlazor bump and font change (the reason the Store markup-snapshot port was cut); the markup snapshots and the axe scans cover the regressions that matter.
    • §9 baseline ratchet keeping openapi-baseline.json in step with the build documents: a route added after seeding is protected once the baseline is regenerated in the pull request that changes the contract, which is the documented procedure.
    • §5, §7, §11 and §21 (implementation) without a lever: each would need new machinery (a slice re-layout, a broker-fed projection, a threat model, axe on every PR) against no observed defect.
    • Batch feedback submit, session-scoped live-poll endpoint, and the GetOpenPollsHandler two-queries-per-poll shape: at 1-3 open polls and a 10-question feedback form these are a handful of requests per user, and each fix needs a new command, endpoint or authorization decision.
    • Optional cross-service broker-tier coverage for speaker analytics and the bookmark-count gRPC read: optional by its own record, and no defect has surfaced on either path.
  • /profile/sessions reachable by URL, menu link Organizer-only (§25): the framework's Signed-in devices page lists the caller's own sign-ins and revokes them per device, so it stays open to every signed-in account ([Authorize], Common Sessions.razor:1); Layout:SessionsNavRequiredRole keeps its menu link to Organizers so the attendee menu holds to the conference surfaces (MMCA.ADC.UI.Web/appsettings.json:36, ADC commit 48691548). Documented at adc-NavigationFlow.md:726.
  • Single-region deployment (no multi-region failover): accepted in infra/DISASTER-RECOVERY.md, with the single SQL server and single Container Apps environment as accepted single points of failure; the measured load does not justify the cost.
  • No interactive OpenAPI UI (Scalar/Swagger): the h2c-only REST services are not browser-reachable directly; a Gateway-routed UI is a small follow-up if wanted.
  • Integration events published post-commit carrying DB-generated IDs: intentional, since the event must carry the persisted identity.
  • Constructor-dependency ceilings (§1): AuthenticationService is a cohesive auth facade and is accepted as-is. The service ceiling is 10 (:28, set for AuthenticationService), the controller ceiling 10 (Tests/Architecture/MMCA.ADC.Architecture.Tests/Cqrs/ConstructorDependencyCountTests.cs:56) and the handler ceiling 7 (:65), the last set at the widest handler (ResetPasswordHandler, SubmitQuestionHandler). SessionsController sits at 10 on a dispensation documented in the test's XML doc (ConstructorDependencyCountTests.cs:44-53): its tenth dependency, IEntityQueryService<Event, ...>, exists only for the BR-86 advisory X-Warning header on create, which the framework's fixed create-handler signature cannot set. Lower it the moment a framework change makes 9 true.
  • Vertical slice, deliberate layered-by-project hybrid (§5): cross-cutting concerns run in the decorator pipeline, slice cohesion is held by the CI-gated SliceCohesionTests, and the hybrid is the accepted choice that caps implementation at 8. It does not absorb enforcement-coverage gaps, which stay schedulable.
  • [~] Design-system residuals (§20), Common-side and watch item: eight !important overrides live in MMCA.Common's shared app.css (:189, :209-210, :297, :302, :307-308, :314, each with a mobile or a11y rationale; a generalized selector at :206); these are MMCA.Common changes, not ADC-local. ADC's own markup carries zero inline styles: layout and sizing values ride semantic classes in the module stylesheet (MMCA.ADC/Source/Modules/Conference/MMCA.ADC.Conference.UI/wwwroot/adc-ui.css:75 .text-center-cell through :308 .presenter-main, global because they land on markup MudBlazor renders), and Tests/Architecture/MMCA.ADC.Architecture.Tests/Ui/InlineStyleTests.cs:22 pins the count at zero with no allow-list (:13), so a reintroduced style=, Style= or CellStyle= fails the CI architecture tier.
  • Deploy E2E gate is chromium-only, ui-scoped and cross-engine by recency: e2e-gate invokes one browser (deploy.yml:806, browsers: '["chromium"]' at :820) and runs only on UI-affecting diffs (:817, rationale :808-816); firefox and webkit run on alternating weekly single-engine nightly legs (Monday firefox, Thursday webkit) with continue-on-error on the schedule (e2e.yml:147). The cross-browser-freshness step (deploy.yml:923; freshness in deploy.needs :1159, required :1193) requires an in-window green proof per engine, so cross-engine coverage is enforced by recency rather than per-deploy execution: a UI deploy can ship code never run on firefox or webkit until the next weekly leg goes red and stale. The deploys e2e-gate skips still ship a PR-tested tree: the required build-and-test runs the CI.slnf unit, architecture and bUnit tier (deploy.yml:366), and branch protection enforces admins with strict up-to-date required checks (ADC PR #258), so the merged tree is the tree it ran on. A deliberate Actions-minute trade-off.
  • E2E runs Blazor Server mode in CI: e2e.yml pins E2E_FORCE_SERVER=true; forcing WebAssembly in CI fails wholesale on the 2-core hosted runner (every fresh browser context pays a cold WASM boot and the readiness probe is satisfied during prerender). E2E_FORCE_WASM stays supported for local runs. Retrying WASM in CI needs all three of: a WASM-aware readiness signal, amortized per-context WASM boot, and a raised job cap. A faster self-hosted runner makes the suite fail more (Blazor Server circuits drop under sustained fast-suite load), so do not repeat the rejected test-side and circuit-config tweaks; the only paths that work are a slower pace or dedicated UI-host CPU.
  • Freshness-gate break-glass: the four recency gates (dr-freshness, load-freshness, cross-service-freshness, cross-browser-freshness, the four steps of the freshness job) each accept a skip_freshness_gates workflow_dispatch input with a required justification (declared at deploy.yml:13-18, passed to the shared Common freshness-gate action at :855, :874, :904, :943), so each proof is bypassable by an operator. This qualifies the "enforced deploy precondition" language used for §6, §12, §22, §29 and §33.
  • No ordering stamp on the speaker-link handlers (§10): neither SpeakerLinkedToUser nor User carries an ordering stamp, so a late redelivery (second-level redelivery reaches 3600 s on Azure Service Bus, Common MessageBusSettings.cs:211) can overwrite LinkedSpeakerId (SpeakerLinkedToUserHandler.cs:59). The limit is recorded as accepted in the code (:25-27): an organizer repairs it by re-linking, and SpeakerUnlinkedFromUserHandler.cs:56 guards the stale direction.
  • Pseudo-localization breadth deferred (§27): broadening PseudoLocalizationTests beyond its three public pages (PseudoLocalizationTests.cs:41-43, public by design per :25) across the authenticated authoring surface is worth 1 weighted point and costs authenticated-login plumbing plus text-expansion and overflow assertions across roughly 54 of the 57 routable @page files, the weakest cost-to-benefit ratio on either band. The assertion is a rendered-layout property, so bUnit cannot close it, and every browser case added to the deploy-gating chromium e2e-gate is paid on every future deploy. A citable culture-aware-formatting violation also stands against the lift. §27 keeps its band row at implPriority 1, but the lever is not to be re-proposed without new evidence. Re-open triggers: a second locale beyond es, any RTL locale, or a reported layout regression on an authenticated page. Maturity 4 is unaffected (TranslationCompletenessTests + LocalizedTextConventionTests, both in MMCA.ADC.CI.slnf:58, run at deploy.yml:366).
  • BR-130 room double-booking overlap check is a SOFT guard: SessionRoomScheduling.ValidateRoomAssignmentAsync is a read-then-write advisory check with no transaction, lock or DB exclusion constraint, so two concurrent organizer writes for the same room with overlapping windows can both pass. Each alternative is rejected on evidence: a transactional re-check cannot close the race below SERIALIZABLE (which escalates to key-range or table locks across the Sessionize import path with no (RoomId, StartsAt) index); IDistributedLock's contract forbids sole-guard use on a correctness invariant and degrades to in-process when Redis is absent; sp_getapplock needs an EF/SqlClient reference the Application layer forbids. The spec's BR-130 promises only the cross-event room check (422); a double-booking is repairable by editing either session. Documented in the class XML doc (ADC PR #94). Re-open triggers: organizer concurrency materially above today's handful, a real double-booking incident, or a cheap DB-level range-exclusion capability.
  • §16 is scored, not N/A: rubric v2's "N/A in all three repos" claim (ADR-110) does not hold for ADC, because AI session scoring calls a model in production. §16 sits at M4/I10, governed by ADR-111 and ADR-120. The ADR-110 wording is a Website-side correction for /update-adrs. The former Maintainability & Evolvability criteria score under §34 (coupling, tech-debt register), §32 (lockstep upgrades) and §33 (onboarding).
  • [accepted] SQL data plane on public network access, password path retained (§11 / §17): publicNetworkAccess: 'Enabled' (infra/main.bicep:861) plus the AllowAzureServices 0.0.0.0 firewall rule (:867), with azureADOnlyAuthentication deliberately not set (:876, the password path kept as a dormant fallback) and the sqlAdminPassword parameter always supplied (deploy.yml:1296); managed-identity SQL auth is opt-in through the useManagedIdentitySql repo variable (bicep default false, :36) and active in production. Moving onto private endpoints is the VNet and private-endpoint epic, which recreates the Container Apps environment, so it is deferred by design and documented-accepted in infra/SQL-MANAGED-IDENTITY.md; it is the 9→10 rung, not a scheduled lever. The three AllowAnonymous internal gRPC surfaces (BookmarkCountsGrpcService at Engagement.Service/Program.cs:347, LiveChannelGrpcService at Notification.Service/Program.cs:293, and EventLiveValidationGrpcService at Conference.Service/ConferenceGrpcEndpoints.cs:32, rationale :22-31, with "a fresh token at join" recorded as its follow-up at :30-31) are accepted with their compensating controls: bounded payloads, no user ids on the wire, no Gateway route. Service-to-service identity holds on the other four (ConferenceGrpcEndpoints.cs:20, Engagement.Service/Program.cs:355, Notification.Service/Program.cs:301, Identity.Service/Program.cs:373). Re-open triggers: a VNet-integrated environment landing for another reason, or a data-plane exposure finding.
  • [accepted] §11 threat model not scheduled (TD-26, effort M): a committed STRIDE-style model per trust boundary (Gateway edge, per-service gRPC, outbox/broker, Key Vault, mobile client) under infra/ or Docs/ is cut by decision; do not re-schedule it without asking. The rubric's "no threat model" red flag stays citable (no threat-model file exists in the repo), so §11 holds at implementation 8; the mitigations such a model would enumerate are enforced individually (the CI-gated Tests/Architecture/MMCA.ADC.Architecture.Tests/Api/AnonymousEndpointTests.cs:21 allowlist, the gating supply-chain job, the compensating controls above). Re-open triggers: an explicit request for the model, a compliance requirement that names one, or a security finding a maintained model would have caught.
  • [accepted] TD-15 (effort L) · topology collapse to one host and one database: the framework supports collapsing the distributed topology into a modular monolith with no application-code rewrite (AddBrokerMessaging falls back to the in-process bus, DataSourceResolver collapses the per-module sources onto one database, ModuleLoader boots all four modules in one host; MMCA.Helpdesk is the single-host proof). The recorded estimate is a run cost of ~$190-220/mo cut to roughly a third, with the gRPC partial-failure class eliminated (ACA ~$110-130, SQL ~$25, Log Analytics ~$25, Redis ~$13 at Balanced B0, Service Bus ~$10 at Standard); those dollar figures are unverified against billing. Blocker (deliberate): the distributed topology is the GTM demonstrator of the framework's extract-a-service path. Resolution path when revisited: single service host enabling all modules (Helpdesk pattern), one ADC database via the resolver collapse, drop Service Bus/Redis/Gateway from main.bicep, point the UI at the host, and re-run the k6 capacity proof at the collapsed tier.
  • [closed] Batch the feedback submit: EventFeedback/SessionFeedback send one HTTP POST per answered question over a 10-question set, each its own transaction, outbox write and audit stamp. Needs a batch upsert command and endpoint. Closed 2026-10-19 (see Closed as over-engineering).
  • [closed] Session-scoped live-poll management endpoint: SessionLive fetches event-wide polls and filters client-side. Not a type-compatible swap (GetEventPollsAsync returns LivePollDTO, the session-scoped call LivePollResultsDTO), and the event list is organizer-only with speakers falling back on 403, so it needs an authorization decision too. Closed 2026-10-19 (see Closed as over-engineering).
  • [closed] GetOpenPollsHandler issues two queries per poll: the N+1 shape SessionQuestionViewBuilder removed, one file over. Theoretical at ADC's scale (1-3 open polls). Closed 2026-10-19 (see Closed as over-engineering).
  • UI read cache not adopted (§12): the Conference UI services do not opt into IUiReadCache (zero occurrences in ADC Source); the opt-in caused e2e-gate staleness failures. This is the §12 9→10 residual.
  • [closed] Optional cross-service broker-tier coverage (§14): the Testcontainers RabbitMQ + SQL round-trip tier (MMCA.ADC.CrossService.IntegrationTests, nightly cross-service-tests.yml, its recency gating deploys through cross-service-freshness) does not cover speaker analytics or the Conference→Engagement bookmark-count gRPC reads. Optional, not scheduled.
  • Dedicated rate-limit integration fixtures skipped: the WebApplicationFactory hosts neutralize the limiter; proving the 300/min cap and the per-IP registration throttle needs a tight-limit fixture variant, low value at this volume. Revisit only if abuse is observed.
  • Refresh cookie keeps SameSite=Lax, not Strict: Lax is load-bearing for the SSR-prerender path ([Authorize] pages opened in a new tab, on F5 or from an external link are cross-site top-level navigations that Strict would strip the cookie from, forcing a spurious /login bounce, the scenario ADR-022's cookie scheme exists to serve). CSRF is covered by the /auth/session/token endpoint's Sec-Fetch-Site check, POST-only, and SameSite=Lax. The session/refresh cookie lives 7 days (SessionCookieJar, aligned to the refresh-token lifetime).
  • Data export excludes feedback answers (§30): the export aggregates Identity, Engagement (bookmarks, submitted questions) and Notification (inbox items); event and session feedback answers live in the Conference database and would need a third gRPC edge. Out of scope by decision.
  • No in-band k6 capacity proof (§12): running the k6 tier inside every deploy loads production on every deploy at a peak of about 67 users, so the monthly read-only run (load-test.yml:23) plus the load-freshness recency gate (deploy.yml:863, 35-day window :872) is the accepted posture. §12 keeps its maturity-band row at priority 2 and holds at maturity 3 by this decision; do not re-propose the in-band proof without asking.
  • No AsyncAPI document for the integration events (§9, TD-24's async half): ADC has no external event consumers, and the frozen contract snapshot (Tests/Architecture/MMCA.ADC.Architecture.Tests/Contracts/IntegrationEventContractTests.cs:9) already guards the seven events byte-exactly. With the OpenAPI baseline-diff half shipped (see Resolved), TD-24 is closed rather than deferred; the parameter-aware detector and the baseline ratchet under #9 are the remaining §9 lever.
  • No WASM code-splitting (§23): already evaluated and rejected in Common (common-RemediationBacklog.md #23). The image-optimization and EFIGS ICU shard half of #23 stays open.
  • §18 code-behind cap stays at 400: lowering MaxCodeBehindLines to 300 (UIArchitectureConventionTestsBase.cs:22) would force about twelve extractions at once; only the files near 400 are extracted (TD-16), and the cap is kept at 400.
  • Cached bookmark-count gRPC read kept (§7): Engagement owns the bookmark counts, and Conference reads them in one batched gRPC call behind the 60-second BookmarkCountsCache policy with broker-driven eviction (Source/Services/MMCA.ADC.Conference.Service/Program.cs:294-301), so the read is not a chatty-sync defect. Replacing it with an event-fed Conference-side projection (an outbox event, a projection table, an ADR-114 backfill) adds a contract, a table, a backfill and drift risk for one implementation point, and is rejected. §7 holds at implementation 8 on the remaining bidirectional pairs and call loop; do not re-propose the projection.
  • ImageSharp 3.1.12 advisory hold (§15 / §32): five NuGetAuditSuppress items (Directory.Build.props:55-59, rationale :46-53) cover the 3.1.12 advisories: the package arrives only transitively through MMCA.Common.Infrastructure, the fix ships only in 4.1.2, which the v3 license pin rules out, and the single decode path is Common's ImageSharpImageProcessor. The hold mirrors MMCA.Common's; revisit it with MMCA.Common on the next ImageSharp release.

Resolved

# Category M / I Evidence
§1 SOLID Principles 4 / 9 Constructor ceilings gate CI (Tests/Architecture/MMCA.ADC.Architecture.Tests/Cqrs/ConstructorDependencyCountTests.cs:28,:56,:65); four oversized controllers split by sub-resource with no URL, verb or authorization change (Conference.API/Controllers/Events/EventLifecycleController.cs:32, Speakers/SpeakerLinksController.cs:33, Sessions/SessionCalendarController.cs:26, Engagement.API/Controllers/LivePollVotingController.cs:35); LSP guard against NotSupportedException/NotImplementedException overrides (Governance/SubstitutabilityTests.cs:9)
§2 Design Patterns 4 / 9 Result-returning factories and per-entity sync strategies (Identity.Domain/Users/User.cs:226; five *SyncStrategy.cs)
§3 Clean Architecture 4 / 9 Layer, purity and transport-at-edge rules in the CI arch gate (MMCA.ADC.CI.slnf:58); MicroserviceExtractionTests enforce ADR-007
§4 Domain-Driven Design 4 / 9 Email value object inside Event (Event.cs:59, invariant at :189, :265); no public setters in Conference.Domain; EntityConventionTests.cs:3
§5 Vertical Slice Architecture (maturity) 4 / 8 Slice cohesion is a CI merge gate (SliceCohesionTests.cs:8); TD-19: the enforced map covers all four modules (AdcArchitectureMap.cs:51-54)
§6 CQRS & Event-Driven 4 / 9 Frozen integration-event contract (IntegrationEventContractTests.cs:9-14); TD-02: EnableInbox=true on all four services and the broker round-trip gating deploys through cross-service-freshness
§8 Data Architecture 4 / 9 TD-03 concurrency round-trip (UpdateEventHandler.cs:34, OrganizerConcurrencyTests.cs:44); TD-04 outbox fidelity (OutboxFidelityTests); model-drift gate (deploy.yml:450); SoftDeleteFidelityTests
§9 API & Contract Design (maturity) 4 / 8 OpenAPI per service at /openapi/v1.json; v1.0 deprecated / v2.0 pair (ServiceInfoController.cs:18-19, ApiVersioningTests.cs:14); extraction ADR-008
§11 Security (maturity) 4 / 8 Global rate limiter (MMCA.Common AddCommonRateLimiting, 300 req/min per authenticated user); authz access-denied matrices gating every PR; all runtime secrets in RBAC Key Vault via the shared UAMI; AnonymousEndpointTests.cs:21
§11 / §12 UI host hardening 4 / 8, 3 / 9 The hardening kit consumed from MMCA.Common.UI.Web.Hardening (Source/Hosts/UI/MMCA.ADC.UI.Web/Program.cs:31), host wiring pinned by Tests/Hosts/MMCA.ADC.UI.Web.Tests/UiRateLimitingTests.cs:29 (ADR-088)
§12 Performance & Scalability (implementation) 3 / 9 TD-21: k6 green through the synthetic-traffic bypass; hub fan-out at maxReplicas: 2 (TwoReplicaHubFanOutTests.cs:49); backpressure on all four services (Conference.Service/appsettings.json:51-52); Redis-backed output cache across replicas (Conference.Service/Program.cs:195)
§12 Performance program 3 / 9 Junction/lookup reads cached with parent-tag eviction; scoring evicts conference:sessions only; bookmark counts on a 60s policy; feedback answers out of public payloads; speaker session lists filtered server-side; LivePoll(SessionId, Status) indexed; conference-read-load.js:56-57 exercises the includeChildren=true shapes
§13 Observability & Operability 4 / 9 Alert-to-runbook pairing gate (ObservabilityConventionTests.cs, MMCA.ADC.CI.slnf:58); SLO workbook (infra/main.bicep:798); per-alert runbook (infra/OPERATIONS.md:13)
§14 Testability & Test Strategy 4 / 9 Per-service WebApplicationFactory integration tiers gating every PR; TD-05 coverage floor 55.5% (deploy.yml:406); cross-service handler tests (CrossServiceUserRegisteredTests.cs, CrossServiceSpeakerLinkTests.cs)
§15 Best Practices & Code Quality (hygiene) 4 / 8 Five dated, argued NuGetAuditSuppress items, the ImageSharp hold (Directory.Build.props:55-59, see Deliberate and accepted); the patched SSH.NET pin (Directory.Packages.props:83); every global NoWarn code justified (Directory.Build.props:16-31); the test-tier NoWarn sets central in suffix-conditioned PropertyGroups with per-code reasons (Directory.Build.props:65-104), none in any test csproj
§16 AI-Native Application Architecture 4 / 10 TD-22 golden replay and prompt hash (GoldenReplayTests.cs:183, PromptContractTests.cs:57); TD-23 input envelope and redaction; TD-25 trace source (MMCA.Common.Aspire/Extensions.cs:185); output guardrail at the boundary (SessionScoreResponseGuardrail.cs:74, pinned by ConferenceAiGuardrailsRegistrationTests.cs:81); permanent kill switch (ConferenceFeatures.cs:38)
§17 DevOps & Deployment 4 / 9 Passwordless managed-identity SQL auth in production; ACR pulled via UAMI with the admin user disabled; post-deploy smoke plus auto-rollback (deploy.yml:1492)
§18 UI Architecture & Components (maturity) 4 / 8 UI-architecture fitness gate (UIArchitectureConventionTests.cs:12); TD-13 splits of SessionSelectionDashboard and SpeakerDetail
§19 State Management & Data Flow 4 / 9 State-management fitness gate (StateManagementConventionTests.cs:11); live-accessor UnsavedChangesGuard on all create and detail forms (UnsavedChangesGuard.razor:32)
§20 Design System & UI Consistency 4 / 9 Brand-token guard (Ui/BrandColorTokenTests.cs:12); zero inline styles, pinned by Ui/InlineStyleTests.cs:22; one shared ADCHome.razor.css on var(--mmca-primary) (:249); Secondary family guarded Common-side (MMCA.Common.UI.Tests/Theme/BrandColorTokenTests.cs:38,:60)
#10 item Contract snapshot 3 / 8 Generic arguments spelled out and keyed by the [EventName] wire identity (Common ArchitectureRules.Events.cs:45,:82-89; ADC IntegrationEventContractTests.cs:17)
#10 item [EventName] on every integration event 3 / 8 Common Bases/Contracts/EventConventionTestsBase.cs:21 (rule at ArchitectureRules.Events.cs:33-41), inherited by ADC Tests/Architecture/MMCA.ADC.Architecture.Tests/Contracts/EventConventionTests.cs:3
TD-24 OpenAPI breaking-change baseline (§9) 4 / 8 All four services diff against a committed Contract/openapi-baseline.json (Conference OpenApiContractTests.cs:29, Identity :39, Engagement :30, Notification :31) through Common's opt-in baseline mode (OpenApiContractTestsBase.cs:80, :117-153; ADC #279); the AsyncAPI half is recorded under Deliberate and accepted
#31 item cost-guard header comment 4 / 8 cost-guard.yml:3-10 states the surge is IaC (conferenceMode) switched by CONFERENCE_MODE_UNTIL on every deploy, the first deploy after the window being the revert
§21 Accessibility (implementation) 3 / 8 axe inside the deploy-gating e2e-gate (AccessibilityTests.cs:23); outlined Warning surfaces moved to AA-passing variants on six sites
§23 Front-End Performance (maturity) 4 / 8 Core Web Vitals budgets asserted inside the deploy-gating e2e-gate (WebVitalsTests.cs:39)
#24 item BR-122 cross-field mirror 4 / 8 SessionFormModel.cs:87-92 on the create (SessionCreate.razor.cs:162-166) and edit (SessionDetail.razor.cs:186-191) paths; server error surfaced (SessionCreate.razor.cs:178)
§24 Forms, Validation & UX Safety (maturity) 4 / 8 FormsConventionTests.cs:18,32; TD-14: <ErrorSummary> on 21 of 21 MudForm files (EventCreate.razor:41) with a render-level bUnit assertion
§25 Navigation & Information Arch (maturity) 4 / 8 Navigation contract gate (Api/NavigationContractTests.cs:63-76,:79-104 against adc-NavigationFlow.md:639); route-authorization gates (ManagementRouteAuthorizationTests.cs:19, IdentityRouteAuthorizationTests.cs:16)
§26 Front-End Security 4 / 9 Cookie-only refresh and in-memory access token; single-use OAuth code exchange (OAuthController.ExchangeAsync); enforced CSP and hardened headers on UI host and Gateway (Source/Hosts/MMCA.ADC.Gateway/Program.cs:118,168); TD-09 SecurityHeadersTests
§27 Internationalization (i18n) 4 / 8 en-US + es with 68 resx pairs; TranslationCompletenessTests.cs:12 and LocalizedTextConventionTests.cs in CI; pseudo-localization tier on the public pages (PseudoLocalizationTests.cs:41-43)
§28 Front-End Testing & Quality 4 / 9 TD-06/TD-07: chromium E2E/axe suite as the deploy gate; markup-snapshot tier (ComponentsSnapshotTests.cs:27); TD-20: a deploy that skips the UI-scoped e2e-gate still ships a PR-tested tree (required build-and-test, deploy.yml:220, with branch protection enforcing admins and strict up-to-date required checks, ADC PR #258; rationale :812-816), and the UI-scoping itself is recorded under Deliberate and accepted; ManagementRouteAuthorizationTests
§29 Resilience & Business Continuity 4 / 9 TD-10: drilled PITR restores (infra/DISASTER-RECOVERY.md:212-217) under a 60-minute drill timeout (dr-drill.yml:49); dr-freshness deploy gate (deploy.yml:844); LTR on all four DBs (infra/main.bicep:984); GracefulShutdownTests
§30 Compliance, Privacy & Governance 4 / 9 PII inventory with purpose, lawful basis and retention (PRIVACY.md:225) synced to the [Pii] markers by Privacy/PiiInventoryTests.cs; anonymize-in-place erasure (Identity.Domain/Users/User.cs:510, DeleteUserHandler.cs:47-49); PII-free logging and erasure proven by ErasureAndPiiLoggingTests; export aggregated across Identity, Engagement and Notification; DataResidencyTests
§31 Cost Efficiency / FinOps (maturity) 4 / 8 cost-guard in deploy.needs (deploy.yml:783, :1159); monthly RG budget (infra/main.bicep:819); commonTags on every billable resource (infra/main.bicep:203) plus a per-service service tag on the six container apps and the four databases (:1705, :991), pinned by CostTagConventionTests.cs:10 (a CostTagConventionTestsBase subclass)
§32 Dependency & Supply-Chain 4 / 9 TD-01: 67 committed lock files restored --locked-mode (deploy.yml:297); blocking audit and SBOM gates (:608, :623); FrameworkVersionConsistencyTests
§33 Developer Experience & Inner Loop 4 / 9 TD-17: authoritative Service Bus emulator tier (cross-service-tests.yml:142) required by cross-service-freshness (deploy.yml:884); ADC_BROKER=servicebus local profile (AppHost/Program.cs:91); full getting-started README
§34 Architecture Governance & Docs 4 / 9 The ADR set, range and count owned by ../adr/README.md; 54 CI-gated architecture-test classes (Tests/Architecture/MMCA.ADC.Architecture.Tests/), framework totals owned by MMCA.Common/FACTS.md:51; this ledger carries every TD-NN item
A-1..A-7 Defect audit - Cancellation propagates past scoring catches; partial score JSON rejected; duplicate category-item guards; case-insensitive UserRole.IsOrganizer(string?) in DeleteUserHandler/ExportUserDataHandler; category-distribution soft-delete predicate aligned
- Code health - BR-49 eligibility in one place (SessionStatuses in Conference.Shared); 15 read-only query handlers on GetReadRepository; calendar exports on the injected clock; ADCHome on the UI services; shared test builders and MMCA.Common.Testing fakes; LatestLoadGuard on five detail pages