mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-10 00:14:38 +00:00
Add Patrol safe workflow row guidance
This commit is contained in:
parent
477d549efd
commit
f0c81de00e
5 changed files with 77 additions and 3 deletions
|
|
@ -77,9 +77,9 @@ or historical proof/counting for resolved-only work.
|
|||
Assistant handoffs, approvals, verification, and manual controls. A
|
||||
compact row-owned issue scaffold may translate the same finding, approval,
|
||||
and workflow fields into problem, affected resource, consequence, checked
|
||||
evidence, next step, and verification state, but it must stay attached to
|
||||
the active issue row and must not become a separate proof, trust, or status
|
||||
strip. Because a
|
||||
evidence, safe workflow step, next step, and verification state, but it must
|
||||
stay attached to the active issue row and must not become a separate proof,
|
||||
trust, or status strip. Because a
|
||||
contextual Assistant handoff from that workflow is still a first-party Patrol
|
||||
starter for the same governed journey, it must record content-free workflow
|
||||
prompt activity through the shared marker route with the `pulse_patrol`
|
||||
|
|
|
|||
|
|
@ -996,8 +996,10 @@ describe('aiFindingPresentation', () => {
|
|||
expect(aiFindingPresentationSource).toContain("label: 'Affected'");
|
||||
expect(aiFindingPresentationSource).toContain("label: 'Why it matters'");
|
||||
expect(aiFindingPresentationSource).toContain("label: 'What Pulse checked'");
|
||||
expect(aiFindingPresentationSource).toContain("label: 'Safe workflow'");
|
||||
expect(aiFindingPresentationSource).toContain("label: 'Recommended next step'");
|
||||
expect(aiFindingPresentationSource).toContain("label: 'Verification'");
|
||||
expect(aiFindingPresentationSource).toContain('getPatrolFindingWorkflowSummary');
|
||||
expect(findingsPanelSource).not.toContain('Patrol trust summary');
|
||||
expect(findingsPanelSource).not.toContain('Patrol proof strip');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -131,6 +131,11 @@ describe('getPatrolFindingRowScaffold', () => {
|
|||
label: 'What Pulse checked',
|
||||
value: 'CPU stayed above the configured warning threshold during the scheduled Patrol check.',
|
||||
},
|
||||
{
|
||||
id: 'workflow',
|
||||
label: 'Safe workflow',
|
||||
value: 'Review evidence, decide the next action, and verify any outcome before closing.',
|
||||
},
|
||||
{
|
||||
id: 'next-step',
|
||||
label: 'Recommended next step',
|
||||
|
|
@ -164,6 +169,21 @@ describe('getPatrolFindingRowScaffold', () => {
|
|||
label: 'Recommended next step',
|
||||
value: 'A governed fix is waiting for an approve or reject decision.',
|
||||
});
|
||||
expect(
|
||||
getPatrolFindingRowScaffold(
|
||||
makeRowScaffoldFinding({
|
||||
investigationStatus: 'needs_attention',
|
||||
investigationOutcome: 'fix_queued',
|
||||
}),
|
||||
approvals,
|
||||
Date.parse('2026-06-30T08:07:00Z'),
|
||||
)?.items,
|
||||
).toContainEqual({
|
||||
id: 'workflow',
|
||||
label: 'Safe workflow',
|
||||
value:
|
||||
'Review evidence first; no change runs until the proposed fix is approved, then Patrol verifies the outcome.',
|
||||
});
|
||||
expect(
|
||||
getPatrolFindingRowScaffold(
|
||||
makeRowScaffoldFinding({
|
||||
|
|
@ -180,6 +200,21 @@ describe('getPatrolFindingRowScaffold', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('keeps executed fixes on the verification step before closure', () => {
|
||||
expect(
|
||||
getPatrolFindingRowScaffold(
|
||||
makeRowScaffoldFinding({
|
||||
investigationStatus: 'completed',
|
||||
investigationOutcome: 'fix_executed',
|
||||
}),
|
||||
)?.items,
|
||||
).toContainEqual({
|
||||
id: 'workflow',
|
||||
label: 'Safe workflow',
|
||||
value: 'The governed action ran; review follow-up evidence before closing the issue.',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not scaffold non-Patrol, runtime setup, or resolved findings', () => {
|
||||
expect(
|
||||
getPatrolFindingRowScaffold(makeRowScaffoldFinding({ source: 'threshold' })),
|
||||
|
|
|
|||
|
|
@ -683,6 +683,7 @@ export type PatrolFindingRowScaffoldItemId =
|
|||
| 'next-step'
|
||||
| 'problem'
|
||||
| 'verification'
|
||||
| 'workflow'
|
||||
| 'why';
|
||||
|
||||
export interface PatrolFindingRowScaffoldItem {
|
||||
|
|
@ -752,6 +753,31 @@ function getPatrolFindingVerificationSummary(
|
|||
return 'No fix has run yet.';
|
||||
}
|
||||
|
||||
function getPatrolFindingWorkflowSummary(
|
||||
workflow: FindingPatrolWorkflowPresentation | undefined,
|
||||
): string {
|
||||
if (!workflow) {
|
||||
return 'Review evidence, decide the next action, and verify any outcome before closing.';
|
||||
}
|
||||
|
||||
switch (workflow.stage) {
|
||||
case 'approval':
|
||||
return workflow.label === 'Approve or reject'
|
||||
? 'Review evidence first; no change runs until the proposed fix is approved, then Patrol verifies the outcome.'
|
||||
: 'Recover the queued fix before any action can run, then verify the outcome after a decision.';
|
||||
case 'verification':
|
||||
return 'The governed action ran; review follow-up evidence before closing the issue.';
|
||||
case 'attention':
|
||||
return 'Review the blocked or failed step before approving another change or resolving manually.';
|
||||
case 'investigating':
|
||||
return 'Patrol is explaining the issue and preparing the next decision point.';
|
||||
case 'recorded':
|
||||
return 'Patrol recorded the outcome; use history if you need the completed trail.';
|
||||
case 'paused':
|
||||
return 'Patrol will return this issue to the workflow when the reminder expires.';
|
||||
}
|
||||
}
|
||||
|
||||
const getNonEmptyPresentationText = (
|
||||
value: string | undefined,
|
||||
fallback: string,
|
||||
|
|
@ -827,6 +853,11 @@ export function getPatrolFindingRowScaffold(
|
|||
'Patrol recorded this from the current check.',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'workflow',
|
||||
label: 'Safe workflow',
|
||||
value: getPatrolFindingWorkflowSummary(workflow),
|
||||
},
|
||||
{
|
||||
id: 'next-step',
|
||||
label: 'Recommended next step',
|
||||
|
|
|
|||
|
|
@ -626,6 +626,7 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
|||
await expect(issueSummary.getByText("Affected")).toBeVisible();
|
||||
await expect(issueSummary.getByText("Why it matters")).toBeVisible();
|
||||
await expect(issueSummary.getByText("What Pulse checked")).toBeVisible();
|
||||
await expect(issueSummary.getByText("Safe workflow")).toBeVisible();
|
||||
await expect(issueSummary.getByText("Recommended next step")).toBeVisible();
|
||||
await expect(issueSummary.getByText("Verification")).toBeVisible();
|
||||
await expect(
|
||||
|
|
@ -642,6 +643,11 @@ test.describe("Monitor-first Patrol workbench browser contract", () => {
|
|||
"CPU stayed above the configured warning threshold during the scheduled Patrol check.",
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
issueSummary.getByText(
|
||||
"Review evidence first; no change runs until the proposed fix is approved, then Patrol verifies the outcome.",
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
issueSummary.getByText(
|
||||
"A governed fix is waiting for an approve or reject decision.",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue