Recs rebuild WS1b-2: wire Apply (consent) + Mute actions#1064
Merged
Conversation
Wire the Recommendations tab's Apply and Mute buttons live (Dashboard-only), replacing the WS1b-1 disabled "Available in the next update" affordances. Apply mirrors the proven AlertDetailWindow path exactly: fail-closed server resolution, the handler-for-fact gate, then RemediationApplyService.ApplyAsync with a confirm callback that news up RemediationConfirmWindow. Destructive fixes (RCSI / clear-plan) set RequiresInformedConsent, so the two-sided acknowledge-each-risk gate renders automatically (no special-casing); the disclosure draws on the persisted action's carried figures (finding: null). The shared RemediationApplyService is threaded MainWindow -> ServerTab -> RecommendationsContent, the same instance the Alerts tab uses. Mute is engine-only: the lower-level story mute keyed on (serverId, story_path_hash) via SqlServerFindingStore.MuteStoryAsync, since the card holds a RecommendationItem rather than a full AnalysisFinding. Both Apply and Mute refresh on success so the action-log outcome / dropped row is reflected. RecommendationItem gains a StoryPath (populated from finding.StoryPath in the reader) for the mute record's operator-facing label. 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.
Scope (Dashboard-only)
Wires the Recommendations tab's Apply and Mute buttons live, replacing the WS1b-1 disabled "Available in the next update" affordances. Apply mirrors the existing
AlertDetailWindowpath exactly — fail-closed server resolution, the handler-for-fact gate, thenRemediationApplyService.ApplyAsyncwith a confirm callback that news upRemediationConfirmWindow. Destructive fixes (RCSI / clear-plan) setRequiresInformedConsent, so the two-sided acknowledge-each-risk gate renders automatically (no special-casing); the inaction disclosure draws on the persisted action's carried figures (finding: null, exactly as on the alert path). Mute is engine-only via the lower-level story mute. No Lite changes (apply stack is Dashboard-only).File:line changelog
Dashboard/Services/Recommendations/RecommendationItem.cs:133-139— addStoryPath(operator-facing label for the mute record).Dashboard/Services/Recommendations/RecommendationsReader.cs:123— populateStoryPath = finding.StoryPathinMapEngineFinding(the store already readsstory_pathback).Dashboard/Controls/RecommendationsContent.xaml.cs:57-70—_remediationApplyServicefield + publicRemediationApplyServiceproperty (mirrorsAlertsHistoryContent).:9-12, :20— addusing System.Linq; System.Threading; …Services.Remediation;.:273-334—ApplyButton_Click/RunApplyAsync:ResolveServer(_serverConnection.Id, ServerName),HasHandlerFor,ApplyAsync(item.Remediation, resolution.Server, item.CopyPasteSql, operatorIdentity, sourceRef, ConfirmAsync, ct, finding: null); refresh on run.:342-352—ConfirmAsync:Dispatcher.InvokeAsync→new RemediationConfirmWindow(request, resolution.ResolvedByName, resolution.Reason) { Owner = Window.GetWindow(this) }.:358-390—FormatApplyStatus(compact status-strip summary).:401-445—MuteButton_Click/RunMuteAsync:_findingStore.MuteStoryAsync(serverId, item.StoryPathHash, item.StoryPath ?? hash, …); refresh on success.:451-464—ResolveOperatorIdentity(mirrorsAlertDetailWindow).Dashboard/Controls/RecommendationsContent.xaml:55-78— Apply button enabled +Click="ApplyButton_Click"(removedIsEnabled="False"+ActionsDisabledReasontooltip); new Mute button bound toShowMute+Click="MuteButton_Click".Dashboard/Controls/RecommendationsViewModel.cs— refreshedShowApply/ShowMutedoc comments; removed now-deadActionsDisabledReasonproperty +ActionsDisabledTooltipconst.Dashboard/ServerTab.xaml.cs:35-39— forwardingRemediationApplyServiceproperty →RecommendationsTab.Dashboard/MainWindow.xaml.cs:659—serverTab.RemediationApplyService = _remediationApplyService;(same instance as the Alerts tab).Dashboard.Tests/RemediationTests.cs:1835-1836— addServerTab.xaml.cs+Controls/RecommendationsContent.xaml.csto the sanctionedRemediationApplyServiceallowlist guard (GatedEntry_ReferencedOnlyBySanctionedUiPath).Dashboard.Tests/RecommendationDeduperTests.cs:399, :423— assertStoryPathmaps (engine) / is null (legacy).Dashboard.Tests/RecommendationsViewModelTests.cs— builder gainsstoryPath; replaced the obsolete disabled-tooltip test withEngineCard_ExposesMuteKeyInputs_HashAndPath+LegacyCard_HasNoMuteKeyInputs.Test counts (real, this machine)
dotnet build PerformanceMonitor.sln -c Debug→ 0 errors (1 pre-existing unrelated CS0649 warning inRemediationTests.cs).dotnet test Dashboard.Tests --no-build→ 315 passed, 0 failed, 0 skipped.dotnet test Lite.Tests --no-build→ 360 passed, 0 failed, 0 skipped (incl. the order-dependent Azure-auth test, which passed in-suite here).Needs visual / integration verification (live DB + WPF)
The unit tests cover the VM enablement predicates (
ShowApply/ShowMute) and the reader'sStoryPathmapping. The runtime behaviors need the orchestrator to launch the Dashboard:config.remediation_action_logrow is written and the recommendations refresh.config.analysis_mutedrow (serverId + story_path_hash + story_path).🤖 Generated with Claude Code