Align Patrol findings footer recency

Patrol findings empty-state timing now consumes the canonical recency label so scoped activity is rendered consistently as activity rather than generic or full-patrol recency.
This commit is contained in:
rcourtman
2026-03-25 12:22:30 +00:00
parent 77d496f70e
commit a884b8da17
4 changed files with 18 additions and 1 deletions
@@ -173,6 +173,10 @@ formatting future schedule timestamps through generic relative-time helpers;
otherwise the findings footer can contradict the header by rendering the same
next scheduled patrol as `just now` while the main Patrol shell correctly
shows a multi-hour countdown.
That footer must also use the canonical Patrol recency label rather than a
generic `Last:` prefix, so scoped-only recent activity is rendered as
`Last activity` and does not silently revert to patrol/full-verification
language in the findings surface.
When Patrol is currently running, that strip should still stay factual rather
than switching to another verdict label: the runtime may add an explicit
in-progress indicator, but the primary activity label remains recent activity
@@ -61,6 +61,7 @@ interface FindingsPanelProps {
showControls?: boolean;
nextPatrolAt?: string;
lastPatrolAt?: string;
lastPatrolLabel?: string;
patrolIntervalMs?: number;
scopeResourceIds?: string[];
scopeResourceTypes?: string[];
@@ -1161,7 +1162,7 @@ export const FindingsPanel: Component<FindingsPanelProps> = (props) => {
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>Last: {formatTime(props.lastPatrolAt!)}</span>
<span>{props.lastPatrolLabel ?? 'Last'}: {formatTime(props.lastPatrolAt!)}</span>
</div>
</Show>
<Show when={props.nextPatrolAt}>
@@ -163,6 +163,11 @@ describe('aiFindingPresentation', () => {
expect(findingsPanelSource).toContain('<CountdownTimer targetDate={props.nextPatrolAt!} prefix="Next: " />');
expect(findingsPanelSource).not.toContain('Next: {formatTime(props.nextPatrolAt!)}');
});
it('uses the supplied canonical patrol recency label in the footer', () => {
expect(findingsPanelSource).toContain("{props.lastPatrolLabel ?? 'Last'}: {formatTime(props.lastPatrolAt!)}");
expect(findingsPanelSource).not.toContain('<span>Last: {formatTime(props.lastPatrolAt!)}</span>');
});
});
describe('sourceColors', () => {
@@ -4,10 +4,16 @@ import { ApprovalBanner, PatrolStatusBar, RunHistoryPanel } from '@/components/p
import { getFindingSeverityToneClasses } from '@/utils/aiFindingPresentation';
import { formatRelativeTime } from '@/utils/format';
import { formatTriggerReason } from '@/utils/patrolFormat';
import { getPatrolRecencyPresentation } from '@/utils/patrolSummaryPresentation';
import type { PatrolIntelligenceState } from './usePatrolIntelligenceState';
export function PatrolIntelligenceWorkspace(props: { state: PatrolIntelligenceState }) {
const state = props.state;
const recency = () =>
getPatrolRecencyPresentation({
runs: state.patrolRunHistory() ?? [],
lastPatrolAt: state.patrolStatus()?.last_patrol_at,
});
return (
<>
@@ -96,6 +102,7 @@ export function PatrolIntelligenceWorkspace(props: { state: PatrolIntelligenceSt
<FindingsPanel
nextPatrolAt={state.patrolStatus()?.next_patrol_at}
lastPatrolAt={state.patrolStatus()?.last_patrol_at}
lastPatrolLabel={recency().label}
patrolIntervalMs={state.patrolStatus()?.interval_ms}
filterOverride={state.selectedRun() ? 'all' : state.findingsFilterOverride()}
filterFindingIds={state.selectedRunFindingIds()}