From 8fd20f2222b24875c25b259f2d52b95bc910091b Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 8 May 2026 19:59:21 +0100 Subject: [PATCH] Add Explain contextual button to findings Adds a contextual "Explain" entry point next to the existing "Discuss with Assistant" button on every finding card. The new button opens Assistant with the same handoff context (investigation record, operational memory, pending approval, proposed fix) but seeds a different leading sentence: "Explain this Patrol finding... Walk me through what we know, why it matters for the affected workloads, how confident the analysis is, and whether the recommended action is the right next step." This routes the LLM toward an explanatory framing rather than open-ended discussion, matching the user's vision of specific contextual entry points instead of a single generic chat button. Plumbing changes: - New PatrolAssistantFindingIntent type ('discuss' | 'explain') - Optional intent on PatrolAssistantFindingPromptInput and PatrolAssistantFindingHandoffInput - buildPatrolAssistantFindingPrompt switches the leading sentence on intent; downstream context attachment is identical so trust signals (impact, confidence, previous resolved fix, etc.) flow through both paths uniformly. - FindingsPanel extracts the shared handoff into a small openFindingInAssistant(finding, intent) helper used by both handleDiscussWithAssistant and the new handleExplainFinding. Adds two tests: explain-intent prompt has explanation framing while discuss-intent keeps the existing wording, and the FindingsPanel source-text test pins the new button + handler wiring. Updates the patrol-intelligence, frontend-primitives, and api-contracts contracts to pin the contextual-intent rule and the uniform-context-attachment invariant. --- .../v6/internal/subsystems/api-contracts.md | 8 +++++ .../subsystems/frontend-primitives.md | 10 ++++-- .../subsystems/patrol-intelligence.md | 12 ++++++- .../src/components/AI/FindingsPanel.tsx | 33 +++++++++++++++++-- .../AI/__tests__/FindingsPanel.test.ts | 11 +++++++ .../patrolInvestigationContextModel.test.ts | 21 ++++++++++++ .../patrol/patrolInvestigationContextModel.ts | 18 ++++++++-- 7 files changed, 105 insertions(+), 8 deletions(-) diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index 14fda840b..23c711043 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -966,6 +966,14 @@ the canonical monitored-system blocked payload. persisted findings created by older binaries must adopt the freshly-classified impact text on next re-detection rather than preserving the empty value. + The Patrol Assistant handoff carries an optional + `intent` field of type `PatrolAssistantFindingIntent` + (`'discuss' | 'explain'`) so contextual entry points on the finding + surface can route through the same handoff builder while seeding + different leading sentences. The structured handoff context + (investigation record, operational memory, pending approval, + proposed fix, next-step action) must be attached identically across + intents; only the seed prompt's framing varies. `unified.UnifiedFinding` also carries a `previous_resolved_fix_summary` operational-memory field captured at regression time from diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 42a6d243f..b7462e054 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -942,9 +942,13 @@ prompt explain the same operator-facing priority. investigation-record framing must derive that prompt copy through `frontend-modern/src/features/patrol/patrolInvestigationContextModel.ts` so shared drawer primitives stay shell-owned rather than becoming a - Patrol-specific prompt formatter. That feature-owned presentation - helper is the single emitter for investigation-record `impact` and - `rollback` fields: when an investigation record exists but those fields + Patrol-specific prompt formatter. The Patrol-owned helper exposes a + `PatrolAssistantFindingIntent` parameter ('discuss' | 'explain') so + contextual entry points can vary the seeded leading sentence without + duplicating the structured-context attachment; shared drawer + primitives must not branch on intent themselves. That feature-owned + presentation helper is the single emitter for investigation-record + `impact` and `rollback` fields: when an investigation record exists but those fields are empty, the helper emits explicit `Impact not assessed` and `Rollback not specified` lines into the model-only Patrol finding prompt context so the operator-visible gap is surfaced to Assistant diff --git a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md index 5f7cd341d..f17d42947 100644 --- a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md +++ b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md @@ -258,7 +258,17 @@ Patrol-specific presentation helpers. renders an `Impact:` line between `Description` and `Recommendation`. The same panel also surfaces `investigation_record.confidence` as a badge in the collapsed finding row (next to the investigation outcome - badge) so operators can scan trust without expanding every card. The + badge) so operators can scan trust without expanding every card. + Findings carry contextual Assistant entry points keyed on intent: the + default "Discuss with Assistant" button opens an open-ended chat + handoff, and a parallel "Explain" button opens the same handoff with + a `PatrolAssistantFindingIntent='explain'` seed that asks the LLM to + walk through what we know, why it matters, how confident the + analysis is, and whether the recommended action is appropriate. Both + buttons must route through `buildPatrolAssistantFindingHandoff` so + the structured context (investigation record, operational memory, + pending approval, proposed fix, next-step action) is attached + uniformly; only the leading sentence differs by intent. The badge palette is provided by `getInvestigationConfidenceBadgeClasses` in `frontend-modern/src/utils/aiFindingPresentation.ts`: high is reassuringly emphasized, medium is neutral, low is a soft amber. diff --git a/frontend-modern/src/components/AI/FindingsPanel.tsx b/frontend-modern/src/components/AI/FindingsPanel.tsx index b2e9aec0f..89ca307f5 100644 --- a/frontend-modern/src/components/AI/FindingsPanel.tsx +++ b/frontend-modern/src/components/AI/FindingsPanel.tsx @@ -498,8 +498,10 @@ export const FindingsPanel: Component = (props) => { } }; - const handleDiscussWithAssistant = async (finding: UnifiedFinding, e: Event) => { - e.stopPropagation(); + const openFindingInAssistant = async ( + finding: UnifiedFinding, + intent: 'discuss' | 'explain', + ) => { await aiIntelligenceStore.loadPendingApprovals(); const subject = getFindingSubjectPresentation(finding).label; const title = getFindingTitlePresentation(finding).label; @@ -537,10 +539,21 @@ export const FindingsPanel: Component = (props) => { proposedFix, investigationRecord: finding.investigationRecord, nextStepAction, + intent, }); aiChatStore.openWithPrompt(handoff.prompt, handoff.context); }; + const handleDiscussWithAssistant = async (finding: UnifiedFinding, e: Event) => { + e.stopPropagation(); + await openFindingInAssistant(finding, 'discuss'); + }; + + const handleExplainFinding = async (finding: UnifiedFinding, e: Event) => { + e.stopPropagation(); + await openFindingInAssistant(finding, 'explain'); + }; + const handleOpenPlanInAssistant = (finding: UnifiedFinding, plan: RemediationPlan, e: Event) => { e.stopPropagation(); const subject = getFindingSubjectPresentation(finding).label; @@ -1022,6 +1035,22 @@ export const FindingsPanel: Component = (props) => { Add Note +