Improve Patrol Assistant handoff approval boundary

This commit is contained in:
rcourtman
2026-05-08 12:25:32 +01:00
parent 31d07e74ce
commit a6ea15bfa9
5 changed files with 48 additions and 19 deletions
@@ -369,10 +369,11 @@ the canonical monitored-system blocked payload.
remediation-plan handoffs must use the same boundary for frontend-authored
prompts: plan status, risk, step labels, and command counts are allowed,
while raw command and rollback command payloads remain in governed action
surfaces. Frontend finding-discussion handoffs that carry any live approval,
proposed-fix, fix outcome, or remediation-plan reference must force a
surfaces. Frontend Patrol finding-discussion handoffs must force a
request-local approval-required Assistant mode instead of inheriting the
user's persistent autonomous control setting. Frontend-visible Patrol
user's persistent autonomous control setting; live approval, proposed-fix,
fix-outcome, and remediation-plan references only add structured action
posture, they are not the trigger for the boundary. Frontend-visible Patrol
briefing payloads may include short suggested prompts, but those prompts
must be derived from the same safe action posture, evidence, recurrence, and
remediation-plan metadata as the briefing and must never carry raw approval,
@@ -999,11 +999,11 @@ prompt explain the same operator-facing priority.
control level. Patrol remediation-plan drawer handoffs must use the same
primitive boundary: plan title/status/risk, step labels, and command counts
may enter Assistant context; raw command and rollback command payloads must
stay in the governed remediation/action panel. Finding discussion handoffs
that reference a live approval, proposed fix, fix outcome, or remediation
plan must also pass `autonomousMode:false` as a request-local override so
the drawer shows approval-required posture without mutating the persistent
Assistant control setting.
stay in the governed remediation/action panel. All Patrol finding
discussion handoffs, including context-only findings without a live approval
or proposed fix, must pass `autonomousMode:false` as a request-local
override so the drawer shows approval-required posture without mutating the
persistent Assistant control setting.
11. Keep shared filter primitives coherent with route-owned option hydration.
Feature shells such as `frontend-modern/src/features/infrastructure/`
must keep a route-owned canonical option visible in shared selects like
@@ -199,10 +199,10 @@ Patrol-specific presentation helpers.
prerequisites, rollback, and verification, while command and rollback command
text stays in the governed remediation or approval surface. Generic finding
discussion handoffs must also force request-local approval-required mode for
any non-empty Patrol `finding_id`, including findings that reference a live
approval, proposed fix, fix outcome, or remediation plan, so default
autonomous Assistant settings cannot bypass the Patrol action-governance
boundary. The assembled handoff must still pass
any non-empty Patrol `finding_id`, including context-only findings and
findings that reference a live approval, proposed fix, fix outcome, or
remediation plan, so default autonomous Assistant settings cannot bypass the
Patrol action-governance boundary. 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
@@ -1250,6 +1250,40 @@ describe('patrolInvestigationContextModel', () => {
).toBe(false);
});
it('keeps context-only Patrol finding handoffs approval scoped', () => {
const handoff = buildPatrolAssistantFindingHandoff({
id: 'finding-context-only',
title: 'Provider connection issue',
subject: 'Patrol runtime',
description: 'Pulse Patrol could not maintain a healthy provider connection.',
severity: 'warning',
findingStatus: 'active',
loopState: 'detected',
resourceId: 'pulse-patrol-runtime',
resourceName: 'Patrol runtime',
resourceType: 'service',
});
expect(handoff.context).toMatchObject({
targetType: 'service',
targetId: 'pulse-patrol-runtime',
findingId: 'finding-context-only',
autonomousMode: false,
context: {
source: 'pulse-patrol-finding',
findingId: 'finding-context-only',
resourceId: 'pulse-patrol-runtime',
resourceName: 'Patrol runtime',
resourceType: 'service',
actionReferenceCount: 0,
},
});
expect(handoff.context.handoffActions).toBeUndefined();
expect(handoff.context.handoffContext).toContain(
'Operator Boundary: This Patrol finding handoff is model-only context',
);
});
it('builds an operator briefing from current finding facts before a Patrol record exists', () => {
expect(
buildPatrolAssistantFindingBriefing({
@@ -608,12 +608,6 @@ export function buildPatrolAssistantFindingHandoff(
const resource = buildPatrolFindingHandoffResource(input);
const handoffResources = resource ? [resource] : [];
const handoffActions = buildPatrolAssistantFindingHandoffActions(input);
const requiresApprovalMode = patrolAssistantFindingHandoffRequiresApprovalMode({
investigationOutcome: input.investigationOutcome,
remediationId: input.remediationId,
pendingApproval: input.pendingApproval,
investigationRecord: input.investigationRecord,
});
return {
prompt: buildPatrolAssistantFindingPrompt({
@@ -630,7 +624,7 @@ export function buildPatrolAssistantFindingHandoff(
targetType: resource?.type,
targetId: resource?.id,
findingId: findingId || undefined,
autonomousMode: requiresApprovalMode ? false : undefined,
autonomousMode: false,
handoffContext: buildPatrolAssistantFindingModelContext(input),
handoffResources: handoffResources.length > 0 ? handoffResources : undefined,
handoffActions: handoffActions.length > 0 ? handoffActions : undefined,