mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 10:35:51 +00:00
Add attention reasons to Assistant briefings
This commit is contained in:
@@ -369,13 +369,13 @@ profile and assignment columns, but embedded table framing must route through
|
||||
lifecycle transition. Unified finding lifecycle facts, latest lifecycle
|
||||
event briefing lines, and detailed lifecycle context included in Assistant
|
||||
handoff context remain Patrol/AI review metadata and must not become agent
|
||||
lifecycle state or fleet-control authority. Primary finding recency,
|
||||
evidence, verification, and operator-decision facts in the operator briefing
|
||||
and related root-cause or correlated finding summaries resolved for the same
|
||||
Assistant handoff follow that same isolation rule, including any recency or
|
||||
latest lifecycle facts attached to those related summaries: they may explain
|
||||
Patrol context to the model and seed model-only handoff resources, but they
|
||||
are not enrollment evidence,
|
||||
lifecycle state or fleet-control authority. Primary finding attention,
|
||||
recency, evidence, verification, and operator-decision facts in the operator
|
||||
briefing and related root-cause or correlated finding summaries resolved for
|
||||
the same Assistant handoff follow that same isolation rule, including any
|
||||
recency or latest lifecycle facts attached to those related summaries: they
|
||||
may explain Patrol context to the model and seed model-only handoff
|
||||
resources, but they are not enrollment evidence,
|
||||
command-websocket identity, agent command grants, or auto-approval policy.
|
||||
Structured Assistant handoff action references from the same Patrol finding
|
||||
remain AI/runtime review metadata only; lifecycle code must not treat
|
||||
|
||||
@@ -207,10 +207,11 @@ runtime cost control, and shared AI transport surfaces.
|
||||
resolved timestamps, recurrence, regression, and recent lifecycle events so
|
||||
Assistant explains the current Patrol record rather than only the original
|
||||
investigation narrative. The operator briefing must surface the primary
|
||||
finding's current recency facts, bounded evidence snapshot, verification
|
||||
summary, and explicit operator decision framing before investigation
|
||||
guidance, may surface the latest lifecycle event as the current handoff
|
||||
state, and detailed lifecycle events must stay in a bounded
|
||||
finding's current attention reason, recency facts, bounded evidence
|
||||
snapshot, verification summary, and explicit operator decision framing
|
||||
before investigation guidance, may surface the latest lifecycle event as
|
||||
the current handoff state, and detailed lifecycle events must stay in a
|
||||
bounded
|
||||
`[Finding Lifecycle Context]` block with an explicit model-only boundary.
|
||||
Assistant runtime may also hydrate canonical
|
||||
resource-policy context for those handoff resources, using the same
|
||||
@@ -963,12 +964,13 @@ top-N slices.
|
||||
Assistant finding handoffs now also receive a model-only operator briefing
|
||||
derived from the current unified finding and structured Patrol investigation
|
||||
record before the lower-level finding context. That briefing must summarize the
|
||||
finding, resource, priority, current recency facts, bounded evidence and
|
||||
verification summaries, investigation confidence, recommended next step,
|
||||
operator decision framing, latest lifecycle event, and governed action posture
|
||||
as operator guidance, while leaving detailed lifecycle history, current
|
||||
resource-state, timeline, related-finding, and action-audit hydration in the
|
||||
existing canonical AI runtime handoff builders. Related root-cause and
|
||||
finding, resource, priority, current attention reason, current recency facts,
|
||||
bounded evidence and verification summaries, investigation confidence,
|
||||
recommended next step, operator decision framing, latest lifecycle event, and
|
||||
governed action posture as operator guidance, while leaving detailed lifecycle
|
||||
history, current resource-state, timeline, related-finding, and action-audit
|
||||
hydration in the existing canonical AI runtime handoff builders. Related
|
||||
root-cause and
|
||||
correlated finding records may be summarized from current unified finding state,
|
||||
including their recency and latest lifecycle facts, and may seed their own
|
||||
handoff resources for canonical policy, state, topology, and timeline
|
||||
|
||||
@@ -784,14 +784,14 @@ the canonical monitored-system blocked payload.
|
||||
snoozed/dismissed/suppressed status, detection/last-seen/resolution
|
||||
timestamps, recurrence/regression facts, and recent lifecycle events, so API
|
||||
consumers do not reduce Assistant context to an outdated investigation
|
||||
summary. The briefing must carry the primary finding's current recency facts,
|
||||
bounded evidence snapshot, verification summary, and explicit operator
|
||||
decision framing before investigation guidance and may carry the latest
|
||||
lifecycle event as the current handoff state, while the detailed lifecycle
|
||||
list must stay bounded and model-only. Chat execution may also resolve
|
||||
root-cause and correlated finding IDs from that current unified finding into
|
||||
compact related-finding summaries and structured handoff resources. Those
|
||||
related summaries may include the
|
||||
summary. The briefing must carry the primary finding's current attention
|
||||
reason, recency facts, bounded evidence snapshot, verification summary, and
|
||||
explicit operator decision framing before investigation guidance and may
|
||||
carry the latest lifecycle event as the current handoff state, while the
|
||||
detailed lifecycle list must stay bounded and model-only. Chat execution may
|
||||
also resolve root-cause and correlated finding IDs from that current unified
|
||||
finding into compact related-finding summaries and structured handoff
|
||||
resources. Those related summaries may include the
|
||||
related record's current recency and latest lifecycle facts, but those
|
||||
related records remain model-only explanation context and must not become
|
||||
saved user text, disclosure authority, lifecycle authority, approval
|
||||
@@ -2935,10 +2935,10 @@ The `/api/ai/chat` finding handoff contract must also include a model-only
|
||||
`[Operator Briefing]` generated from the unified finding and structured Patrol
|
||||
investigation record before detailed `[Finding Context]`. The briefing is the
|
||||
canonical operator-facing frame for Assistant: it carries the finding summary,
|
||||
resource, priority, current recency facts, bounded evidence and verification
|
||||
summaries, investigation confidence, recommended next step, and
|
||||
operator-decision framing plus approval/proposed-fix posture without raw
|
||||
command text, and the downstream chat service then hydrates live resource
|
||||
resource, priority, current attention reason, current recency facts, bounded
|
||||
evidence and verification summaries, investigation confidence, recommended next
|
||||
step, and operator-decision framing plus approval/proposed-fix posture without
|
||||
raw command text, and the downstream chat service then hydrates live resource
|
||||
state, timeline, and action audit context around that same handoff.
|
||||
Patrol run-history serialization and persistence must also preserve full field
|
||||
parity across API responses and restart boundaries, including
|
||||
|
||||
@@ -131,10 +131,10 @@ Patrol-specific presentation helpers.
|
||||
Assistant handoffs from Patrol findings must also include a concise operator
|
||||
briefing derived from the unified finding and structured investigation record
|
||||
before the detailed finding context, so Assistant leads with the current risk,
|
||||
recency, evidence snapshot, verification summary, conclusion, latest
|
||||
lifecycle event, recommended next step, explicit operator decision framing,
|
||||
and governed approval/proposed-fix posture instead of behaving like a generic
|
||||
chat over a pasted incident dump. The assembled handoff must still pass
|
||||
attention reason, recency, evidence snapshot, verification summary,
|
||||
conclusion, latest lifecycle event, recommended next step, explicit operator
|
||||
decision framing, and governed approval/proposed-fix posture instead of
|
||||
behaving like a generic chat over a pasted incident dump. The assembled handoff must still pass
|
||||
through the Assistant runtime's
|
||||
resource-policy sanitizer before prompt injection, so Patrol-owned prose
|
||||
cannot leak governed resource names, IDs, aliases, nodes, paths, or
|
||||
|
||||
@@ -427,13 +427,13 @@ bypass the API fail-closed execution gate.
|
||||
review metadata only. Unified finding lifecycle facts, latest lifecycle
|
||||
event briefing lines, and detailed lifecycle context carried by the same
|
||||
handoff remain Patrol/AI review metadata and must not become backup recency,
|
||||
restore support, or storage-local lifecycle state. Primary finding recency,
|
||||
evidence, verification, and operator-decision facts in the operator
|
||||
briefing and related root-cause or correlated finding summaries resolved
|
||||
for that Assistant handoff are also adjacent AI/runtime explanation context
|
||||
only, including any recency or latest lifecycle facts attached to those
|
||||
related summaries; storage and recovery surfaces must not reinterpret those
|
||||
related records or their seeded handoff resources as backup freshness,
|
||||
restore support, or storage-local lifecycle state. Primary finding
|
||||
attention, recency, evidence, verification, and operator-decision facts in
|
||||
the operator briefing and related root-cause or correlated finding summaries
|
||||
resolved for that Assistant handoff are also adjacent AI/runtime explanation
|
||||
context only, including any recency or latest lifecycle facts attached to
|
||||
those related summaries; storage and recovery surfaces must not reinterpret
|
||||
those related records or their seeded handoff resources as backup freshness,
|
||||
restore eligibility, recovery execution authority, or storage-local
|
||||
capability truth. Current resource-state, source-health,
|
||||
incident, metric, and capability summaries carried by that handoff are also
|
||||
|
||||
@@ -977,6 +977,9 @@ func appendUnifiedFindingOperatorBriefingContext(b *strings.Builder, f *unified.
|
||||
appendChatContextLine(b, "Finding", formatUnifiedFindingBriefingFinding(f))
|
||||
appendChatContextLine(b, "Resource", formatUnifiedFindingBriefingResource(f))
|
||||
appendChatContextLine(b, "Priority", formatUnifiedFindingBriefingPriority(f))
|
||||
if attention := unifiedFindingBriefingAttention(f); attention != "" {
|
||||
appendChatContextLine(b, "Attention Reason", attention)
|
||||
}
|
||||
appendChatContextLine(b, "Recency", formatUnifiedFindingBriefingRecencyFacts(f))
|
||||
appendChatContextLine(b, "Investigation", formatUnifiedFindingBriefingInvestigation(f))
|
||||
if evidence := unifiedFindingBriefingEvidence(f); evidence != "" {
|
||||
@@ -1066,6 +1069,79 @@ func formatUnifiedFindingBriefingPriority(f *unified.UnifiedFinding) string {
|
||||
return strings.Join(parts, "; ")
|
||||
}
|
||||
|
||||
func unifiedFindingBriefingAttention(f *unified.UnifiedFinding) string {
|
||||
if f == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
parts := make([]string, 0, 8)
|
||||
status := unifiedFindingChatStatus(f, now)
|
||||
switch status {
|
||||
case "active":
|
||||
if severity := strings.TrimSpace(string(f.Severity)); severity != "" {
|
||||
parts = append(parts, "active "+severity+" finding")
|
||||
} else {
|
||||
parts = append(parts, "active finding")
|
||||
}
|
||||
case "resolved":
|
||||
if f.RegressionCount > 0 {
|
||||
parts = append(parts, "resolved after prior regression")
|
||||
} else {
|
||||
parts = append(parts, "resolved finding")
|
||||
}
|
||||
case "snoozed":
|
||||
parts = append(parts, "snoozed finding")
|
||||
case "dismissed":
|
||||
parts = append(parts, "dismissed finding")
|
||||
case "suppressed":
|
||||
parts = append(parts, "suppressed finding")
|
||||
}
|
||||
if f.RegressionCount > 0 {
|
||||
parts = append(parts, fmt.Sprintf("regressed %d times", f.RegressionCount))
|
||||
} else if f.TimesRaised > 1 {
|
||||
parts = append(parts, fmt.Sprintf("raised %d times", f.TimesRaised))
|
||||
}
|
||||
if f.LastRegressionAt != nil {
|
||||
parts = append(parts, "last regression "+f.LastRegressionAt.Format(time.RFC3339))
|
||||
}
|
||||
if loopState := strings.TrimSpace(f.LoopState); loopState != "" {
|
||||
parts = append(parts, "loop "+loopState)
|
||||
}
|
||||
|
||||
rec := f.InvestigationRecord
|
||||
outcome := strings.TrimSpace(f.InvestigationOutcome)
|
||||
if rec != nil {
|
||||
outcome = strings.TrimSpace(string(rec.Outcome))
|
||||
if approvalID := strings.TrimSpace(rec.ApprovalID); approvalID != "" {
|
||||
parts = append(parts, "approval "+approvalID)
|
||||
}
|
||||
if rec.ProposedFix != nil && rec.ProposedFix.Destructive {
|
||||
parts = append(parts, "destructive proposed fix")
|
||||
}
|
||||
}
|
||||
switch aicontracts.InvestigationOutcome(outcome) {
|
||||
case aicontracts.OutcomeFixQueued:
|
||||
parts = append(parts, "fix queued for governed review")
|
||||
case aicontracts.OutcomeFixExecuted:
|
||||
parts = append(parts, "fix executed awaiting verification")
|
||||
case aicontracts.OutcomeFixFailed:
|
||||
parts = append(parts, "fix failed")
|
||||
case aicontracts.OutcomeFixVerificationFailed:
|
||||
parts = append(parts, "verification failed")
|
||||
case aicontracts.OutcomeFixVerificationUnknown:
|
||||
parts = append(parts, "verification inconclusive")
|
||||
case aicontracts.OutcomeNeedsAttention:
|
||||
parts = append(parts, "needs operator attention")
|
||||
case aicontracts.OutcomeCannotFix:
|
||||
parts = append(parts, "Patrol cannot safely fix")
|
||||
case aicontracts.OutcomeTimedOut:
|
||||
parts = append(parts, "Patrol timed out")
|
||||
}
|
||||
|
||||
return formatBriefingStringList(parts, 8, "attention facts")
|
||||
}
|
||||
|
||||
func formatUnifiedFindingBriefingRecency(f *unified.UnifiedFinding) string {
|
||||
if f == nil {
|
||||
return ""
|
||||
|
||||
@@ -732,6 +732,7 @@ func TestHandleChat_IncludesInvestigationRecordContext(t *testing.T) {
|
||||
assert.Contains(t, reqArg.HandoffContext, "Finding: High CPU usage (critical, performance, active)")
|
||||
assert.Contains(t, reqArg.HandoffContext, "Resource: web-server (vm) [vm-100] on pve-1")
|
||||
assert.Contains(t, reqArg.HandoffContext, "Priority: critical performance; status active; loop awaiting_approval; raised 3 times; regressed 2 times")
|
||||
assert.Contains(t, reqArg.HandoffContext, "Attention Reason: active critical finding; regressed 2 times; last regression 2026-05-06T12:06:00Z; loop awaiting_approval; approval approval-123; destructive proposed fix; fix queued for governed review")
|
||||
assert.Contains(t, reqArg.HandoffContext, "Recency: detected 2026-05-06T12:00:00Z; last seen 2026-05-06T12:02:00Z; raised 3 times; regressed 2 times; last regression 2026-05-06T12:06:00Z")
|
||||
assert.Contains(t, reqArg.HandoffContext, "Investigation: completed; outcome fix_queued; confidence high; attempts 1")
|
||||
assert.Contains(t, reqArg.HandoffContext, "Evidence Snapshot: metrics: CPU stayed above 95% for 10 minutes")
|
||||
|
||||
@@ -175,6 +175,8 @@ func TestContract_AssistantFindingContextUsesModelOnlyHandoff(t *testing.T) {
|
||||
"[Operator Briefing]",
|
||||
`appendChatContextLine(b, "Recency", formatUnifiedFindingBriefingRecencyFacts(f))`,
|
||||
"formatUnifiedFindingBriefingRecencyFacts(f)",
|
||||
`appendChatContextLine(b, "Attention Reason", attention)`,
|
||||
"unifiedFindingBriefingAttention(f)",
|
||||
`appendChatContextLine(b, "Evidence Snapshot", evidence)`,
|
||||
`appendChatContextLine(b, "Verification", verification)`,
|
||||
"formatInvestigationRecordEvidenceBriefing",
|
||||
|
||||
Reference in New Issue
Block a user