mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 20:25:56 +00:00
Stop Patrol fetching the circuit-breaker status nobody reads (#2704)
The Patrol page fetched `GET /api/ai/circuit/status` on every load, every 60 s poll, every return to the tab, and again in the background after Retry. The store's breaker state had one production reader: the `circuitBreakerStatus` memo in `usePatrolIntelligenceState`, which nothing read and which #2695 removes. An open breaker already reaches the page through `/api/ai/patrol/status` as the `circuit_open` blocked cause, and the paused banner shows its reason. ## Change - `stores/aiIntelligence.ts`: remove the breaker signal, getter, `circuitBreakerStatusSignal`, `loadCircuitBreakerStatus()`, its org-switch reset, and its `loadDashboardData()` entry. - `features/patrol/usePatrolIntelligenceState.ts`: drop the read from the Retry path's background supporting reads. - `api/ai.ts`: remove `AIAPI.getCircuitBreakerStatus` and the `CircuitBreakerStatus` type; nothing else used them. The backend route stays. - Tests: the client's unit tests and the store and page test mocks go. The store bundle test still pins the exact AIAPI read set. The breaker route stubs are removed from seven E2E specs. - Contracts: patrol-intelligence, ai-runtime, api-contracts and frontend-primitives say the browser reads no breaker status and how an open breaker still reaches Patrol. Each note sits next to #2677's correlation note. ## Evidence - Normalized production-build compare: only the removed store loader, signal, getter and client differ in `index.js`, plus the removed supporting read in `AIIntelligence.js`. `check-bundle-size.mjs` passes. - Private demo-mode stacks for the parent and this tree, driven by the same Playwright script at 1440x900 and 390x844. Across Patrol load, tab hide/show and stale-data Retry, the breaker read drops from 1-2 requests per phase to none. Every other endpoint matches, the Open in Assistant handoff is unchanged, and neither build logs a console error. - A forced `circuit_open` Patrol status still shows "Patrol paused / circuit breaker is open" on both builds. - `tsc --noEmit`, eslint, and vitest for `src/stores`, `src/api`, `src/features/patrol`, `src/components/AI`, the Patrol page test and the boundaries test pass, as does the full pre-commit battery. Stacked on #2695 (which stacks on #2687) with #2677 merged in, so until those land this PR shows their commits too. The 2677 owner agreed this read belongs in a follow-up. `git merge-tree` is clean against main, #2677, #2690, #2691 and #2693.
This commit is contained in:
@@ -6761,6 +6761,11 @@ The browser does not read the global learned-correlation list:
|
||||
`/api/ai/intelligence/correlations`, and neither the shared AI intelligence
|
||||
store nor the Patrol page loads it, because no page renders it. The resource
|
||||
drawer reads per-resource correlations from the resource-intelligence payload.
|
||||
The browser does not read `GET /api/ai/circuit/status` either:
|
||||
`frontend-modern/src/api/ai.ts` carries no client for it and the shared AI
|
||||
intelligence store holds no breaker state, because an open breaker already
|
||||
reaches Patrol as the `circuit_open` runtime block cause. The route stays a
|
||||
backend API surface.
|
||||
AI-facing policy metadata must also be cloned through the shared unified-
|
||||
resource policy helper so chat and tools consumers do not maintain their own
|
||||
policy copy logic. Chat mention prefetch now calls that shared helper directly
|
||||
|
||||
@@ -7486,6 +7486,12 @@ no client for it, and neither the dashboard load bundle in
|
||||
fetches it. The resource drawer's correlation evidence comes from the
|
||||
resource-intelligence payload above. The route stays an API surface behind the
|
||||
`ai:execute` scope.
|
||||
The frontend does not load `GET /api/ai/circuit/status` either. No surface
|
||||
renders provider breaker state, so `frontend-modern/src/api/ai.ts` carries no
|
||||
client or `CircuitBreakerStatus` type for it, and neither that dashboard bundle
|
||||
nor the Patrol refresh path fetches it. An open breaker reaches the Patrol page
|
||||
through `/api/ai/patrol/status` as the `circuit_open` blocked cause. The route
|
||||
stays a backend API surface.
|
||||
That correlations route now reads through the canonical AI intelligence
|
||||
facade first, so the handler and its payload keep the detector behind one
|
||||
shared access layer instead of routing directly to Patrol-local correlation
|
||||
|
||||
@@ -6864,7 +6864,8 @@ Patrol stale-data Retry action spin indefinitely or stay disabled
|
||||
while Patrol findings and status remain visible.
|
||||
Neither the Patrol load and poll path (the store's `loadDashboardData` bundle)
|
||||
nor the Retry path's background supporting reads fetch the global
|
||||
learned-correlation list, which no Patrol surface shows.
|
||||
learned-correlation list or the provider circuit-breaker status, neither of
|
||||
which any Patrol surface shows.
|
||||
That same Patrol shell should make scoped trigger policy legible without
|
||||
another navigation step. `frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx`
|
||||
should keep actionable scoped-trigger state legible without promoting
|
||||
|
||||
@@ -2197,10 +2197,13 @@ requester identity as safe metadata, without copying the approval command
|
||||
payload into Assistant.
|
||||
That same store now owns the Patrol dashboard load bundle as well, so the
|
||||
page refresh path stays aligned on a single orchestrated AI bundle instead of
|
||||
repeating the individual summary, findings, circuit-breaker, and approval
|
||||
fetches inline. Neither that bundle nor the Patrol refresh path fetches the
|
||||
global learned-correlation list: the Patrol page renders no correlations, so
|
||||
the store holds no copy of that list.
|
||||
repeating the individual summary, findings, and approval fetches inline.
|
||||
Neither that bundle nor the Patrol refresh path fetches the global
|
||||
learned-correlation list: the Patrol page renders no correlations, so
|
||||
the store holds no copy of that list. Nor do they fetch the provider
|
||||
circuit-breaker status: an open breaker already reaches the page as the
|
||||
`circuit_open` Patrol runtime block on `/api/ai/patrol/status`, so the store
|
||||
holds no separate breaker state.
|
||||
The shared
|
||||
`frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx` and
|
||||
`frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx`
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "1b01ab6e1bbbb12996d39ea4c8b64cf379ce2e01",
|
||||
"verified_at": "2026-10-07T23:54:00Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts": "ef09be2978d90248ad165176d49b131939ea1231b02e4fe239bb01e9da11e4d0"
|
||||
},
|
||||
"routes": [
|
||||
"/patrol",
|
||||
"/patrol (Activity > Finding options and history)",
|
||||
"/patrol (Pulse Assistant drawer)"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 844
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Follow-up receipt for merging the repaired #2695 head (32fad38aa4, which contains current main) into this branch: the hook's final content is that head's hook minus the circuit-breaker supporting read this branch removes.",
|
||||
"Embedded production builds (vite build into internal/api/frontend-modern/dist, go build) of 32fad38aa4 and of this merged tree (1b01ab6e1b), run on pulse-dev because local memory pressure refused heavy work; each on a private DEMO_MODE mock stack with a fresh data dir; the same Playwright script ran against both at both widths.",
|
||||
"Patrol load, tab hide/show, stale-data banner forced by failing GET /api/settings/ai and Retry: GET /api/ai/circuit/status drops from one request per phase on the parent build to none; the banner shows and Retry clears it on both builds; no console errors on either.",
|
||||
"Served active finding, Activity > Finding options and history, Resolve or dismiss > Open in Assistant: the Pulse Assistant drawer opens with the finding attached on both builds.",
|
||||
"Patrol status forced to runtime_state blocked with blocked_cause circuit_open (page.route): both builds show 'Patrol paused' with 'circuit breaker is open'.",
|
||||
"Across the run the only other endpoint difference is one GET /api/updates/plan from the update banner, which fires once the server's background update check completes and does not depend on this change.",
|
||||
"Dist proof on pulse-dev: 187 files each side; after normalising chunk hashes, SRI strings and short identifiers, only the removed store loader, signal, getter and API client differ in index.js and the removed supporting read in AIIntelligence.js."
|
||||
],
|
||||
"interactions": [
|
||||
"Demo auto sign-in; on /patrol open the Activity tab and 'Finding options and history', expand the served finding, open 'Resolve or dismiss', click 'Open in Assistant', close the drawer.",
|
||||
"Dispatch visibilitychange hidden then visible; reload with GET /api/settings/ai failing, wait for the banner, restore the route and click Retry, wait for the banner to clear; reload /patrol with a circuit_open Patrol status.",
|
||||
"Desktop 1440x900 and phone 390x844 contexts; screenshots of the Patrol page, Finding options and history, the Assistant drawer, the stale-data banner and the paused banner inspected."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "3e6e9c3ffb8762b032efab544fda2f339cdb3d96",
|
||||
"verified_at": "2026-10-07T16:28:00Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/api/ai.ts",
|
||||
"frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts",
|
||||
"frontend-modern/src/stores/aiIntelligence.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/api/ai.ts": "8ab01c7c5daac6c2088985423d3bb4584561b67ec306331ff92bf32cb9551738",
|
||||
"frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts": "dba595d1677002a9c55a31b45b4a4b5c600df7b4866afa8eb6bbe6dafbb75503",
|
||||
"frontend-modern/src/stores/aiIntelligence.ts": "d10124d374625cb4a6b2e5b17a357211f73b724473bba9b7a4d0d0d15ccaf6b8"
|
||||
},
|
||||
"routes": [
|
||||
"/patrol",
|
||||
"/patrol (Activity > Finding options and history)",
|
||||
"/patrol (Pulse Assistant drawer)"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 844
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Embedded production builds (vite build into internal/api/frontend-modern/dist, go build) of the parent tree (3e6e9c3ffb, the merge of #2677 into #2695) and this tree, each on a private DEMO_MODE mock stack with a fresh data dir and its own port; the same Playwright script ran against both at both widths.",
|
||||
"Patrol load, tab hide/show, forced stale-data banner and Retry: GET /api/ai/circuit/status goes from 1-2 requests per phase on the parent build to 0 on this build; every other /api endpoint per phase is identical, and neither build logs a console error.",
|
||||
"Served active finding, Activity > Finding options and history, Resolve or dismiss > Open in Assistant: the Pulse Assistant drawer opens with the finding attached on both builds, with identical requests.",
|
||||
"Patrol status forced to runtime_state blocked with blocked_cause circuit_open (page.route): both builds show 'Patrol paused' with 'circuit breaker is open', so breaker state still reaches the page through Patrol status alone.",
|
||||
"Dist proof: 187 files each side; after normalising chunk hashes, SRI strings and short identifiers, only the removed store loader, signal, getter and API client differ in index.js and the removed supporting read in AIIntelligence.js; check-bundle-size.mjs passes."
|
||||
],
|
||||
"interactions": [
|
||||
"Demo auto sign-in; on /patrol open the Activity tab and 'Finding options and history', expand the served finding, open 'Resolve or dismiss', click 'Open in Assistant', close the drawer.",
|
||||
"Dispatch visibilitychange hidden then visible; reload with GET /api/settings/ai failing, then click Retry with the route restored; reload /patrol with a circuit_open Patrol status.",
|
||||
"Desktop 1440x900 and phone 390x844 contexts; screenshots of the Patrol page, Assistant drawer, stale-data banner and paused banner inspected."
|
||||
]
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import { apiFetch, apiFetchJSON } from '@/utils/apiClient';
|
||||
// response-handling of the currently-uncovered AIAPI request functions:
|
||||
// testConnection, resetCostHistory, exportCostHistory, getAnomalies,
|
||||
// getLearningStatus, analyzeKubernetesCluster, approveRemediationPlan,
|
||||
// executeRemediationPlan, rollbackRemediationPlan, getCircuitBreakerStatus.
|
||||
// executeRemediationPlan, rollbackRemediationPlan.
|
||||
// Each test asserts the concrete request (final path + query string + method +
|
||||
// body) and the concrete parsed result for happy paths and each optional /
|
||||
// default-parameter branch arm.
|
||||
@@ -421,58 +421,4 @@ describe('AIAPI branch coverage (request shaping + response handling)', () => {
|
||||
expect(result).toStrictEqual({ success: false });
|
||||
});
|
||||
});
|
||||
|
||||
// --------------------------------------------
|
||||
// getCircuitBreakerStatus
|
||||
// --------------------------------------------
|
||||
describe('getCircuitBreakerStatus', () => {
|
||||
it('GETs /api/ai/circuit/status and returns a closed-breaker payload verbatim', async () => {
|
||||
const payload = {
|
||||
state: 'closed' as const,
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 128,
|
||||
total_failures: 1,
|
||||
};
|
||||
apiFetchJSONMock.mockResolvedValueOnce(payload as any);
|
||||
|
||||
const result = await AIAPI.getCircuitBreakerStatus();
|
||||
|
||||
expect(apiFetchJSONMock).toHaveBeenCalledWith('/api/ai/circuit/status');
|
||||
expect(result).toStrictEqual(payload);
|
||||
});
|
||||
|
||||
it('round-trips an open-breaker payload that blocks patrol', async () => {
|
||||
const payload = {
|
||||
state: 'open' as const,
|
||||
can_patrol: false,
|
||||
consecutive_failures: 5,
|
||||
total_successes: 100,
|
||||
total_failures: 10,
|
||||
};
|
||||
apiFetchJSONMock.mockResolvedValueOnce(payload as any);
|
||||
|
||||
const result = await AIAPI.getCircuitBreakerStatus();
|
||||
|
||||
expect(apiFetchJSONMock).toHaveBeenCalledWith('/api/ai/circuit/status');
|
||||
expect(result).toStrictEqual(payload);
|
||||
expect(result.can_patrol).toBe(false);
|
||||
});
|
||||
|
||||
it('round-trips a half-open breaker probe state', async () => {
|
||||
const payload = {
|
||||
state: 'half-open' as const,
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 100,
|
||||
total_failures: 5,
|
||||
};
|
||||
apiFetchJSONMock.mockResolvedValueOnce(payload as any);
|
||||
|
||||
const result = await AIAPI.getCircuitBreakerStatus();
|
||||
|
||||
expect(apiFetchJSONMock).toHaveBeenCalledWith('/api/ai/circuit/status');
|
||||
expect(result.state).toBe('half-open');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -276,11 +276,6 @@ export class AIAPI {
|
||||
}) as Promise<{ success: boolean }>;
|
||||
}
|
||||
|
||||
// Circuit breaker status
|
||||
static async getCircuitBreakerStatus(): Promise<CircuitBreakerStatus> {
|
||||
return apiFetchJSON(`${this.baseUrl}/ai/circuit/status`) as Promise<CircuitBreakerStatus>;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Investigation Fix Approvals
|
||||
// ============================================
|
||||
@@ -567,14 +562,6 @@ export interface RemediationExecutionResult {
|
||||
completed_at?: string;
|
||||
}
|
||||
|
||||
export interface CircuitBreakerStatus {
|
||||
state: 'closed' | 'open' | 'half-open';
|
||||
can_patrol: boolean;
|
||||
consecutive_failures: number;
|
||||
total_successes: number;
|
||||
total_failures: number;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Investigation Fix Approval Types
|
||||
// ============================================
|
||||
|
||||
@@ -646,7 +646,6 @@ export function usePatrolIntelligenceState() {
|
||||
void Promise.allSettled([
|
||||
aiIntelligenceStore.loadIntelligenceSummary(),
|
||||
aiIntelligenceStore.loadFindings(),
|
||||
aiIntelligenceStore.loadCircuitBreakerStatus(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ const { findingsPanelState, runHistoryState, intelligenceState } = vi.hoisted(()
|
||||
},
|
||||
intelligenceState: {
|
||||
findings: [] as Array<Record<string, unknown>>,
|
||||
circuitBreakerStatus: null as { state: string; consecutive_failures: number } | null,
|
||||
summary: null as {
|
||||
timestamp: string;
|
||||
overall_health: {
|
||||
@@ -254,13 +253,11 @@ vi.mock('@/stores/aiIntelligence', () => {
|
||||
loadFindings: vi.fn().mockResolvedValue(undefined),
|
||||
loadPatrolFindings: vi.fn().mockResolvedValue(undefined),
|
||||
loadIntelligenceSummary: vi.fn().mockResolvedValue(undefined),
|
||||
loadCircuitBreakerStatus: vi.fn().mockResolvedValue(undefined),
|
||||
loadPendingApprovals: vi.fn().mockResolvedValue(undefined),
|
||||
loadDashboardData: vi.fn().mockImplementation(async () => {
|
||||
await Promise.all([
|
||||
store.loadFindings(),
|
||||
store.loadIntelligenceSummary(),
|
||||
store.loadCircuitBreakerStatus(),
|
||||
store.loadPendingApprovals(),
|
||||
]);
|
||||
}),
|
||||
@@ -273,9 +270,6 @@ vi.mock('@/stores/aiIntelligence', () => {
|
||||
get intelligenceSummary() {
|
||||
return intelligenceState.summary;
|
||||
},
|
||||
get circuitBreakerStatus() {
|
||||
return intelligenceState.circuitBreakerStatus;
|
||||
},
|
||||
get patrolPendingApprovals() {
|
||||
return [];
|
||||
},
|
||||
@@ -631,7 +625,6 @@ describe('AIIntelligence entitlement gating', () => {
|
||||
findingsPanelState.latestProps = null;
|
||||
runHistoryState.selection = null;
|
||||
intelligenceState.findings = [];
|
||||
intelligenceState.circuitBreakerStatus = null;
|
||||
intelligenceState.summary = null;
|
||||
|
||||
getPatrolStatusMock.mockResolvedValue(defaultPatrolStatus());
|
||||
|
||||
@@ -6,7 +6,6 @@ vi.mock('@/api/ai', () => ({
|
||||
getRemediationPlans: vi.fn(),
|
||||
getPendingApprovals: vi.fn(),
|
||||
getIntelligenceSummary: vi.fn(),
|
||||
getCircuitBreakerStatus: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -622,8 +621,6 @@ describe('aiIntelligenceStore', () => {
|
||||
incidents_tracked: 0,
|
||||
},
|
||||
});
|
||||
vi.mocked(AIAPI.getCircuitBreakerStatus).mockResolvedValueOnce(null as never);
|
||||
|
||||
await aiIntelligenceStore.loadDashboardData();
|
||||
|
||||
expect(AIAPI.getIntelligenceSummary).toHaveBeenCalledTimes(1);
|
||||
@@ -631,13 +628,13 @@ describe('aiIntelligenceStore', () => {
|
||||
// These are every AIAPI read the bundle makes (Patrol findings come from
|
||||
// the Patrol API module). It does not load the global learned-correlation
|
||||
// list: no page renders it, and the resource drawer reads per-resource
|
||||
// correlations from resource intelligence instead.
|
||||
// correlations from resource intelligence instead. Nor does it load the
|
||||
// provider circuit-breaker status, which no surface renders.
|
||||
const aiReads = Object.entries(AIAPI)
|
||||
.filter(([, read]) => vi.isMockFunction(read) && read.mock.calls.length > 0)
|
||||
.map(([name]) => name)
|
||||
.sort();
|
||||
expect(aiReads).toEqual([
|
||||
'getCircuitBreakerStatus',
|
||||
'getIntelligenceSummary',
|
||||
'getPendingApprovals',
|
||||
'getUnifiedFindings',
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* - Unified findings (alerts + AI findings)
|
||||
* - Canonical intelligence summary
|
||||
* - Remediation plans
|
||||
* - Circuit breaker status
|
||||
*/
|
||||
|
||||
import { createSignal } from 'solid-js';
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
import type { ResourceCriticality } from '@/api/resourceOperatorState';
|
||||
import type {
|
||||
RemediationPlan,
|
||||
CircuitBreakerStatus,
|
||||
UnifiedFindingRecord,
|
||||
InvestigationRecord,
|
||||
ApprovalRequest,
|
||||
@@ -449,14 +447,6 @@ function getPatrolOpenWorkCount(): number {
|
||||
return activeFindingIds.size;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Circuit Breaker
|
||||
// ============================================
|
||||
|
||||
const [circuitBreakerStatus, setCircuitBreakerStatus] = createSignal<CircuitBreakerStatus | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// Canonical Intelligence Summary
|
||||
// ============================================
|
||||
@@ -482,7 +472,6 @@ eventBus.on('org_switched', () => {
|
||||
setPlansError(null);
|
||||
setPendingApprovalsWithExpiryTracking([]);
|
||||
setApprovalsError(null);
|
||||
setCircuitBreakerStatus(null);
|
||||
setIntelligenceSummary(null);
|
||||
});
|
||||
|
||||
@@ -796,12 +785,6 @@ export const aiIntelligenceStore = {
|
||||
}
|
||||
},
|
||||
|
||||
// Circuit Breaker
|
||||
get circuitBreakerStatus() {
|
||||
return circuitBreakerStatus();
|
||||
},
|
||||
circuitBreakerStatusSignal: circuitBreakerStatus,
|
||||
|
||||
// Canonical Intelligence Summary
|
||||
get intelligenceSummary() {
|
||||
return intelligenceSummary();
|
||||
@@ -813,7 +796,6 @@ export const aiIntelligenceStore = {
|
||||
this.loadIntelligenceSummary(),
|
||||
this.loadFindings(),
|
||||
this.loadPatrolFindings(),
|
||||
this.loadCircuitBreakerStatus(),
|
||||
this.loadPendingApprovals(),
|
||||
]);
|
||||
},
|
||||
@@ -830,15 +812,6 @@ export const aiIntelligenceStore = {
|
||||
}
|
||||
},
|
||||
|
||||
async loadCircuitBreakerStatus() {
|
||||
try {
|
||||
const status = await AIAPI.getCircuitBreakerStatus();
|
||||
setCircuitBreakerStatus(status);
|
||||
} catch (e) {
|
||||
logger.error('Failed to load circuit breaker status:', e);
|
||||
}
|
||||
},
|
||||
|
||||
// Initialize - load all data
|
||||
async initialize() {
|
||||
await Promise.all([this.loadDashboardData(), this.loadRemediationPlans()]);
|
||||
|
||||
@@ -349,20 +349,6 @@ async function mockBlockedPatrolRuntimeState(
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/circuit/status", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
state: "closed",
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 42,
|
||||
total_failures: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/approvals", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
@@ -523,20 +509,6 @@ async function mockScopedTriggerPatrolRuntimeState(
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/circuit/status", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
state: "closed",
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 42,
|
||||
total_failures: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/approvals", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
|
||||
@@ -285,20 +285,6 @@ async function mockRetiredQuickstartSurface(
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/circuit/status", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
state: "closed",
|
||||
can_patrol: false,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 0,
|
||||
total_failures: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/approvals", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
|
||||
@@ -541,20 +541,6 @@ test.describe("Patrol Assistant operator briefing", () => {
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/circuit/status", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
state: "closed",
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 42,
|
||||
total_failures: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/approvals", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
|
||||
@@ -476,20 +476,6 @@ async function mockMonitorFirstPatrolWorkbench(
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/circuit/status", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
state: "closed",
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 4,
|
||||
total_failures: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/ai/approvals", async (route) => {
|
||||
const approvals =
|
||||
resolved.pendingApprovalCount > 0
|
||||
|
||||
@@ -493,19 +493,6 @@ async function mockAttention(
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/ai/circuit/status", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
state: "closed",
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 1,
|
||||
total_failures: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/ai/approvals", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
|
||||
@@ -730,15 +730,6 @@ async function routePatrolSupport(
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.route("**/api/ai/circuit/status", async (route) => {
|
||||
await fulfillJSON(route, {
|
||||
state: "closed",
|
||||
can_patrol: true,
|
||||
consecutive_failures: 0,
|
||||
total_successes: 1,
|
||||
total_failures: 0,
|
||||
});
|
||||
});
|
||||
await page.route("**/api/ai/approvals", async (route) => {
|
||||
await fulfillJSON(route, { approvals: [] });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user