B3 Phase 3 PR-A: RCSI destructive-consent privileged core (unregistered)#1040
Merged
Conversation
Implements the privileged core for enabling READ_COMMITTED_SNAPSHOT (RCSI) behind an informed-consent destructive gate, kept UNREACHABLE (dead-code-safe) exactly like the Phase-1/2 PR-A pattern: RcsiHandler is NOT registered and the "Enable RCSI" detail item is NOT emitted. The acknowledge-each-risk dialog rendering, the registry registration, and the cross-surface disclosure are deferred to PR-B. What ships (PR-A): - Shared Analysis lib: RiskItem / RiskDisclosure records; FactRiskDisclosure. GetForAction(action, finding) builds the two-sided, honest-both-directions risk content (risks of changing = fixed prose + validated identifier; risks of NOT changing = fixed framing filled with this server's monitoring figures, with the writer/writer "RCSI won't resolve this" branch and the no-data weak-case baseline). - Drill-down enrichment in BOTH collectors: rcsi_blocking_events (int), rcsi_deadlocks (int), rcsi_reader_writer_pct (int 0-100, nullable), emitted ONLY for RCSI-off databases with identical JSON names + types. Dashboard sources counts from the pre-aggregated collect.blocking_deadlock_stats (O-P3-F) and the reader/writer split from a separate pass over collect.blocking_BlockedProcessReport classified against the FULL lock_mode vocabulary (M-1). Lite emits 0/0/null (M-2). - Executor RCSI arm: DbConfigSetting.ReadCommittedSnapshotOn wired through all four switch sites (m-1: executor SetClauseFor, executor gate ReadDbConfigGateAsync is_read_committed_snapshot_on freshness, service SetClauseFor, FactRemediation .StatementFor) plus the SettingTitle arm (m-2). Reuses the Phase-2 SetDatabaseOptionAsync machinery UNCHANGED. - RcsiHandler : IRemediationHandler (FactKey "RCSI", IsDestructive => true — the first true in the codebase, SupportsUnapply => false, UnapplyAsync throws). Same audit-absent-hard-block / single-connection-gate / one-row-per-attempt skeleton as DbConfigHandler. NOT registered. - FactRemediation.BuildRcsiAction (parallel to the unchanged BuildAction); emits the RCSI action only on rcsi == false (RCSI OFF — M2-1 polarity) + enrichment present. - RemediationConfirmRequest += RequiresInformedConsent + Risks; BuildConfirmRequest signature change to take the resolved handler (+ finding) (B-1) and the RunAsync call-site update. - consent_acknowledged: idempotent COL_LENGTH-guarded ALTER appended after the create block (B-3, NOT a create-body amend) + the four-file plumbing (M-3): RemediationAuditRecord.ConsentAcknowledged, a real bound @consent_acknowledged Bit param in the writer, RcsiHandler BuildRecord => true, DbConfig + force-plan => false. - CoreMachineryMarkers += "RcsiHandler"; all headless §9 unit tests. Security invariants enforced (all reused verbatim from Phase 2 + extended to RCSI): - Identifier safety: the only non-constant token in the RCSI ALTER is the validated+bracketed DB identifier; SET clause is the hardcoded literal "SET READ_COMMITTED_SNAPSHOT ON"; existence validated by a parameterized sys.databases check; same-string ValidatedName bracketed; executor builds its own statement (never the rendered preview). - Single-connection self-gating (R2-MOD-1): gate (existence + fail-closed HAS_PERMS_BY_NAME(@db,'DATABASE','ALTER') + is_read_committed_snapshot_on freshness) and the ALTER on ONE connection; desired state = on => Skip. - Audit-absent hard block before any mutation; one audit row per attempt; per-target independence; no elevation, no InitialCatalog retarget, no credential handling. Tests: Dashboard.Tests 175/175 pass, Lite.Tests 312/312 pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What ships (PR-A — dead-code-safe, unregistered)
The privileged core for enabling
READ_COMMITTED_SNAPSHOT(RCSI) behind an informed-consent destructive gate, kept UNREACHABLE exactly like the Phase-1/2 PR-A pattern:RcsiHandleris NOT registered and the "Enable RCSI" detail item is NOT emitted. The acknowledge-each-risk dialog rendering, registry registration, second-detail-item emission, and cross-surface disclosure are PR-B.PerformanceMonitor.Analysis):RiskItem/RiskDisclosurerecords;FactRiskDisclosure.GetForAction(action, finding)builds the two-sided, honest-both-directions content — risks of changing (fixed prose + validated identifier) and risks of NOT changing (fixed framing filled with the server's monitoring figures, with the writer/writer "RCSI won't resolve this" branch and the no-data weak-case baseline).rcsi_blocking_events(int),rcsi_deadlocks(int),rcsi_reader_writer_pct(int 0–100, nullable), emitted ONLY for RCSI-off databases with identical JSON names + types. Dashboard sources counts from the pre-aggregatedcollect.blocking_deadlock_stats(O-P3-F) and the reader/writer split from a separate pass overcollect.blocking_BlockedProcessReportclassified against the FULLlock_modevocabulary (M-1). Lite emits0/0/null(M-2).DbConfigSetting.ReadCommittedSnapshotOnwired through all four switch sites (m-1) + theSettingTitlearm (m-2); reuses the Phase-2SetDatabaseOptionAsyncmachinery unchanged.RcsiHandler : IRemediationHandler:FactKey => "RCSI",IsDestructive => true(first true in the codebase),SupportsUnapply => false,UnapplyAsyncthrows. Same audit-absent-hard-block / single-connection-gate / one-row-per-attempt skeleton asDbConfigHandler. NOT registered.FactRemediation.BuildRcsiAction(parallel to the unchangedBuildAction): emits the RCSI action only onrcsi == false(RCSI OFF — M2-1 polarity) + enrichment present.RemediationConfirmRequest+=RequiresInformedConsent+Risks;BuildConfirmRequestsignature change to take the resolvedhandler(+finding) (B-1) + theRunAsynccall-site update.consent_acknowledged: idempotentCOL_LENGTH-guarded ALTER appended after the create block (B-3, NOT a create-body amend) + the four-file plumbing (M-3):RemediationAuditRecord.ConsentAcknowledged, a real bound@consent_acknowledgedBit param in the writer,RcsiHandlerBuildRecord => true, DbConfig + force-plan => false.CoreMachineryMarkers+="RcsiHandler"; all headless §9 unit tests.Security invariants enforced (file:line)
SET READ_COMMITTED_SNAPSHOT ON(Dashboard/Services/DatabaseService.Remediation.csSetClauseForarm ~270,BuildAlterStatement~279); existence via parameterizedsys.databasescheck (ReadDbConfigGateAsync~441-452); same-stringValidatedName(~465). Executor builds its own statement — preview never executed.ISNULL(HAS_PERMS_BY_NAME(@db,'DATABASE','ALTER'),0)+is_read_committed_snapshot_onfreshness) and the ALTER on ONE monitoring connection (SetDatabaseOptionAsync~314-388); desired state = on → Skip (new gate arm ~433-440); GateSpid/ExecSpid emitted.RcsiHandlerclones theDbConfigHandlerPermissionDenied path; no elevation, noInitialCatalogretarget, no credential handling.RcsiHandler.RunApplyAsync); one audit row per attempt;consent_acknowledged = trueonly on a gated RCSI apply/skip (RcsiHandler.BuildRecord),falsefor DbConfig (DbConfigHandler.cs) and force-plan (ForcePlanHandler.cs).RcsiHandlerunregistered (RemediationApplyService.cs:55unchanged); RCSI second detail item un-emitted; asserted byRcsi_Handler_IsUnregistered_DeadCodeSafe+CoreMachinery_OnlyReferencedInRemediationCore.BuildRcsiActionemits onJsonValueKind.Falseonly (FactRemediation.cs), mirroring the existingrcsiOffDatabasesscan.S/IS/RangeS-%; writer =X/IX/U/UIX/RangeX-%/RangeI-%;Sch-S/Sch-M/BUexcluded; SQLCASEin the Dashboard collector mirrorsRcsiLockModeClassifier, golden-tested over the full vocabulary.IF COL_LENGTH(...) IS NULL ALTER TABLE ... ADD consent_acknowledged bit NOT NULL CONSTRAINT df_... DEFAULT (0)appended after the guarded create block; correct on both fresh and pre-existing tables.Test results (real)
dotnet build Dashboard/Dashboard.csproj -c Debug— clean (0 warnings, 0 errors).Maintainer real-server pre-merge gate (sql2022, §9 — NOT run here)
is_read_committed_snapshot_on = 1, auditactionset_rcsi_on,prior_value='OFF',consent_acknowledged=1. Re-apply → freshness-skip.🤖 Generated with Claude Code