mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Clarify Patrol scope identities and finding reads
This commit is contained in:
@@ -5705,6 +5705,12 @@ read-only host response when the identity resolves, and returns a successful
|
||||
typed `not_found` result when it does not; a model following the advertised
|
||||
schema must not incur a failed tool call because the executor used a narrower
|
||||
legacy type switch.
|
||||
Scoped Patrol prompt context must describe canonical/source IDs and aliases as
|
||||
identity aliases, not count each value as a distinct infrastructure resource.
|
||||
The exact scoped inventory is the authoritative resource set. Patrol must call
|
||||
`patrol_get_findings` once near the beginning of a run and reuse that snapshot
|
||||
for all lifecycle decisions, avoiding duplicate reads and their extra provider
|
||||
turns on both healthy and faulted runs.
|
||||
Persisted Patrol tool inputs must retain complete structured finding calls up
|
||||
to the bounded 16 KiB record limit so normal evidence-rich findings remain
|
||||
deterministically replayable. If any captured input is nevertheless incomplete
|
||||
|
||||
@@ -1201,13 +1201,13 @@ The seed context includes service identity (from discovery) and reachability dat
|
||||
|
||||
A direct provider-reported failed health check, failed backup, or broken replication state is already confirmed evidence of an operational symptom. Report that symptom even when logs or command execution are unavailable. Use warning/reliability for a failed health check unless the evidence establishes a critical consequence. State that the root cause is unknown and recommend the next safe diagnostic step; never invent a root cause. Missing optional root-cause evidence must not suppress a confirmed symptom-level finding.
|
||||
|
||||
**Step 3 — Report or assess findings.** Report new confirmed issues with patrol_report_finding. For every active finding returned by patrol_get_findings, call patrol_assess_finding exactly once with present, resolved, or uncertain and current evidence. Do not silently skip a known finding: omission is not evidence that it cleared. patrol_resolve_finding remains available for compatibility, but patrol_assess_finding is the complete existing-finding verdict.
|
||||
Always call patrol_get_findings before reporting, assessing, or resolving findings.
|
||||
**Step 3 — Report or assess findings.** Report new confirmed issues with patrol_report_finding. Call patrol_get_findings exactly once near the beginning of the run and reuse that result; do not call it again before the final summary. For every active finding it returned, call patrol_assess_finding exactly once with present, resolved, or uncertain and current evidence. Do not silently skip a known finding: omission is not evidence that it cleared. patrol_resolve_finding remains available for compatibility, but patrol_assess_finding is the complete existing-finding verdict.
|
||||
|
||||
The snapshot eliminates routine data gathering. When a notable signal needs current or historical confirmation, gather enough evidence to distinguish real problems from noise before reporting it.
|
||||
|
||||
## Efficiency Rules
|
||||
- Do NOT call the same tool with the same parameters twice in a single patrol run.
|
||||
- In particular, call patrol_get_findings once and reuse its result for all finding lifecycle decisions in the run.
|
||||
- Keep track of what you've already checked. If you've already retrieved metrics for a resource, use the data you have.
|
||||
- Once direct resource evidence confirms an actionable symptom, report it before pursuing optional root-cause detail.
|
||||
- If a tool reports that a resource lacks the required agent or native capability, do not retry that capability or replace it with a broad inventory scan. Continue with the evidence already available.
|
||||
@@ -1642,7 +1642,7 @@ func (p *PatrolService) assembleSeedWithinBudget(sections []seedSection, budgetT
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func buildScopeSection(scope *PatrolScope, effectiveScopeIDs []string) string {
|
||||
func buildScopeSection(scope *PatrolScope, effectiveIdentityAliases []string) string {
|
||||
if scope == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -1656,16 +1656,17 @@ func buildScopeSection(scope *PatrolScope, effectiveScopeIDs []string) string {
|
||||
sb.WriteString(fmt.Sprintf("Context: %s\n", scope.Context))
|
||||
}
|
||||
if len(scope.ResourceIDs) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("Requested resources: %s\n", strings.Join(scope.ResourceIDs, ", ")))
|
||||
sb.WriteString(fmt.Sprintf("Resolved requested identity aliases: %s\n", strings.Join(scope.ResourceIDs, ", ")))
|
||||
}
|
||||
if len(scope.ResourceTypes) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("Requested resource types: %s\n", strings.Join(scope.ResourceTypes, ", ")))
|
||||
}
|
||||
if len(effectiveScopeIDs) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("Effective scope: %d %s (%s)\n",
|
||||
len(effectiveScopeIDs),
|
||||
seedCountLabel(len(effectiveScopeIDs), "resource", "resources"),
|
||||
seedTruncateOutlierList(effectiveScopeIDs, 8)))
|
||||
if len(effectiveIdentityAliases) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("Model-context identity aliases: %d %s (%s)\n",
|
||||
len(effectiveIdentityAliases),
|
||||
seedCountLabel(len(effectiveIdentityAliases), "alias", "aliases"),
|
||||
seedTruncateOutlierList(effectiveIdentityAliases, 8)))
|
||||
sb.WriteString("Identity aliases are not separate infrastructure resources. Multiple aliases can identify the same scoped resource; do not query each alias. Use the exact scoped inventory rows below as the authoritative resource set.\n")
|
||||
}
|
||||
if scope.AlertIdentifier != "" {
|
||||
sb.WriteString(fmt.Sprintf("Alert Identifier: %s\n", scope.AlertIdentifier))
|
||||
|
||||
@@ -327,6 +327,7 @@ func TestGetPatrolSystemPrompt_IncludesTrustScaffoldingGuidance(t *testing.T) {
|
||||
"Authoring Impact",
|
||||
"Authoring Evidence",
|
||||
"trust anchor",
|
||||
"Call patrol_get_findings exactly once",
|
||||
}
|
||||
for _, want := range required {
|
||||
if !strings.Contains(prompt, want) {
|
||||
|
||||
@@ -57,14 +57,17 @@ func TestPatrolService_buildSeedContext_ScopeSection(t *testing.T) {
|
||||
if !strings.Contains(seed, "Context: CPU alert") {
|
||||
t.Fatalf("expected context in scope section, got:\n%s", seed)
|
||||
}
|
||||
if !strings.Contains(seed, "Requested resources: node-1") {
|
||||
t.Fatalf("expected resource IDs in scope section, got:\n%s", seed)
|
||||
if !strings.Contains(seed, "Resolved requested identity aliases: node-1") {
|
||||
t.Fatalf("expected resolved identity aliases in scope section, got:\n%s", seed)
|
||||
}
|
||||
if !strings.Contains(seed, "Requested resource types: node") {
|
||||
t.Fatalf("expected resource types in scope section, got:\n%s", seed)
|
||||
}
|
||||
if !strings.Contains(seed, "Effective scope: 1 resource (node-1)") {
|
||||
t.Fatalf("expected effective scope in scope section, got:\n%s", seed)
|
||||
if !strings.Contains(seed, "Model-context identity aliases: 1 alias (node-1)") {
|
||||
t.Fatalf("expected model-context aliases in scope section, got:\n%s", seed)
|
||||
}
|
||||
if !strings.Contains(seed, "Identity aliases are not separate infrastructure resources") {
|
||||
t.Fatalf("expected identity/resource boundary in scope section, got:\n%s", seed)
|
||||
}
|
||||
if !strings.Contains(seed, "Alert Identifier: alert-123") {
|
||||
t.Fatalf("expected alert identifier in scope section, got:\n%s", seed)
|
||||
@@ -92,7 +95,7 @@ func TestPatrolService_buildSeedContext_TypeScopedEffectiveScopeSection(t *testi
|
||||
}
|
||||
|
||||
seed, _ := ps.buildSeedContextState(patrolRuntimeStateForTest(ps, state), scope, nil)
|
||||
if !strings.Contains(seed, "Effective scope: 2 resources (node-1, node-2)") {
|
||||
t.Fatalf("expected type-scoped effective scope section, got:\n%s", seed)
|
||||
if !strings.Contains(seed, "Model-context identity aliases: 2 aliases (node-1, node-2)") {
|
||||
t.Fatalf("expected type-scoped model-context aliases, got:\n%s", seed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func TestBuildTriageSeedContext_UnionsExplicitScopeWithFlags(t *testing.T) {
|
||||
t.Fatalf("did not expect unrelated resource %q in triage seed, got:\n%s", unexpected, seed)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(seed, "Effective scope: 2 resources (qemu/102, qemu/107)") {
|
||||
if !strings.Contains(seed, "Model-context identity aliases: 2 aliases (qemu/102, qemu/107)") {
|
||||
t.Fatalf("expected scope section to describe the complete evidence set, got:\n%s", seed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ Returns: {"ok": true, "resolved": true} on success.`,
|
||||
e.registry.registerBuiltin(RegisteredTool{
|
||||
Definition: Tool{
|
||||
Name: agentcapabilities.PatrolGetFindingsToolName,
|
||||
Description: `Get currently active patrol findings. Use this to check what findings already exist before reporting new ones (avoids duplicates) and to identify findings that may need resolution.
|
||||
Description: `Get currently active patrol findings. Call this exactly once near the beginning of a Patrol run, then reuse the result to check what findings already exist before reporting new ones (avoids duplicates) and to identify findings that may need resolution. Do not call it again before the final summary.
|
||||
|
||||
Returns a list of active findings with their IDs, severity, resource, and title.`,
|
||||
InputSchema: InputSchema{
|
||||
|
||||
@@ -758,6 +758,7 @@ func TestPatrolToolsRegistered(t *testing.T) {
|
||||
found := map[string]bool{}
|
||||
var resolveTool Tool
|
||||
var reportTool Tool
|
||||
var getFindingsTool Tool
|
||||
for _, tool := range tools {
|
||||
if tool.Name == "patrol_report_finding" || tool.Name == "patrol_assess_finding" || tool.Name == "patrol_resolve_finding" || tool.Name == "patrol_get_findings" {
|
||||
found[tool.Name] = true
|
||||
@@ -768,6 +769,9 @@ func TestPatrolToolsRegistered(t *testing.T) {
|
||||
if tool.Name == "patrol_report_finding" {
|
||||
reportTool = tool
|
||||
}
|
||||
if tool.Name == "patrol_get_findings" {
|
||||
getFindingsTool = tool
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, found["patrol_report_finding"], "patrol_report_finding should be registered")
|
||||
@@ -782,6 +786,8 @@ func TestPatrolToolsRegistered(t *testing.T) {
|
||||
require.NotEmpty(t, reportTool.Name)
|
||||
assert.Contains(t, reportTool.Description, "failed health check")
|
||||
assert.Contains(t, reportTool.Description, "root cause is unknown")
|
||||
require.NotEmpty(t, getFindingsTool.Name)
|
||||
assert.Contains(t, getFindingsTool.Description, "exactly once")
|
||||
}
|
||||
|
||||
func TestPatrolToolsAvailability(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user