diff --git a/AGENTS.md b/AGENTS.md index 4f9701ba5e..56ead95f11 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -217,7 +217,7 @@ Scoped exception (FN-5819): shared-branch-group members (`branchContext.assignme ### Lazy-Loaded Heavy Views -These 23 views are lazy-loaded via `React.lazy()` with ``. +These 22 views are lazy-loaded via `React.lazy()` with ``. Keep this AGENTS inventory in sync with App lazy imports, AppModals lazy modal imports (`SettingsModal`, `WorkflowNodeEditor`, `SetupWizardModal`), and `packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts`. - `AgentsView` @@ -230,7 +230,6 @@ Keep this AGENTS inventory in sync with App lazy imports, AppModals lazy modal i - `DocumentsView` - `SkillsView` - `ResearchView` -- `ReliabilityView` - `CommandCenter` - `EvalsView` - `TodoView` diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 3eea47adae..4783303126 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -684,7 +684,7 @@ Rendering invariants: - The dashboard browser-layout smoke includes a `[data-smoke="command-center-charts"]` fixture that loads emitted lazy Command Center CSS and verifies representative recharts pie, line, and empty states at mobile (390×844) and desktop breakpoints. The fixture asserts non-zero chart and SVG heights, visible empty-state text, no internal/page horizontal overflow, and no chart-level vertical scroll owner before chart layout changes are considered verified. Data states: -- Overview shows a loading state while core analytics settle, then shows `No usage data yet. Run some agents to populate the Command Center.` only after the selected range has settled with no core usage data. Overview, Tokens, Tools, Activity, Productivity, Team, Ecosystem, GitHub, Signals, and System omit their additive recharts cards in loading/error/empty states, so non-populated data never leaves an empty chart shell. +- Overview shows a loading state while core analytics settle, then shows `No usage data yet. Run some agents to populate the Command Center.` only after the selected range has settled with no core usage data. Overview, Tokens, Tools, Activity, Productivity, Team, Ecosystem, GitHub, Signals, System, and Reliability omit their additive recharts cards in loading/error/empty states, so non-populated data never leaves an empty chart shell. - GitHub issue analytics is local and additive: empty filed/fixed totals keep the stat cards and historical backfill button available while omitting empty chart shells; malformed historical `githubTracking` JSON is skipped instead of breaking the Command Center. - Team analytics renders its shared loading/error/empty states for null or zero-agent responses, omits empty chart shells for zero-value datasets, and keeps the Command Center tab panel as the mobile scroll owner. - System telemetry keeps the previous snapshot visible during refresh failures, renders a first-sample CPU `Sampling…` state without NaN values, shows zero-value task/agent bars for empty collections while omitting the zero-value task-distribution pie, and keeps the Command Center tab panel as the mobile scroll owner. @@ -695,8 +695,8 @@ Data states: Reliability view summarizes in-review pipeline health so operators can spot bounce/merge instability trends without leaving the dashboard. Navigation: -- Desktop: **Header → More views → Reliability** -- Mobile: **More** sheet → **Reliability** +- Desktop and mobile: **Command Center → Reliability** tab +- Legacy persisted `reliability` view state redirects to Command Center so existing browser sessions land on the new tab container instead of an invalid top-level view. Features: - Headline 7-day in-review success rate (derived as `1 - inReviewFailureRate7d`) with color thresholds: success for `≥95%`, warning for `≥90%`, error below `90%`; shows **Insufficient data** when the metric is null @@ -1301,7 +1301,7 @@ Manage project and global secrets directly inside **Settings → Project → Sec ### Lazy-Loaded Heavy Views -These 23 views are lazy-loaded via `React.lazy()` with ``. `prefetchLazyViews()` warms App-level chunks once on mount via `requestIdleCallback`; AppModals lazy modal imports (`SettingsModal`, `WorkflowNodeEditor`, `SetupWizardModal`) are part of the same inventory. **Do not make these eager.** +These 22 views are lazy-loaded via `React.lazy()` with ``. `prefetchLazyViews()` warms App-level chunks once on mount via `requestIdleCallback`; AppModals lazy modal imports (`SettingsModal`, `WorkflowNodeEditor`, `SetupWizardModal`) are part of the same inventory. **Do not make these eager.** - `AgentsView` - `NodesView` @@ -1313,7 +1313,6 @@ These 23 views are lazy-loaded via `React.lazy()` with ` import("./components/ChatView").then((m) => ({ defau const SkillsView = lazy(() => import("./components/SkillsView").then((m) => ({ default: m.SkillsView }))); const MemoryView = lazy(() => import("./components/MemoryView").then((m) => ({ default: m.MemoryView }))); const SecretsView = lazy(() => import("./components/SecretsView").then((m) => ({ default: m.SecretsView }))); -const ReliabilityView = lazy(() => import("./components/ReliabilityView").then((m) => ({ default: m.ReliabilityView }))); const CommandCenter = lazy(() => import("./components/command-center/CommandCenter").then((m) => ({ default: m.CommandCenter }))); const DevServerView = lazy(() => import("./components/DevServerView").then((m) => ({ default: m.DevServerView }))); const _TodoView = lazy(() => import("./components/TodoView").then((m) => ({ default: m.TodoView }))); @@ -146,7 +145,6 @@ function prefetchLazyViews() { void import("./components/SkillsView"); void import("./components/MemoryView"); void import("./components/SecretsView"); - void import("./components/ReliabilityView"); void import("./components/command-center/CommandCenter"); void import("./components/DevServerView"); void import("./components/TodoView"); @@ -1817,16 +1815,6 @@ function AppInner() { ); } - if (taskView === "reliability") { - return ( - - - - - - ); - } - if (taskView === "command-center") { return ( diff --git a/packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts b/packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts index 157c90624b..ed22338b2f 100644 --- a/packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts +++ b/packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts @@ -5,6 +5,9 @@ test enforces that the inventory in AGENTS.md stays in sync with App.tsx (and Ap FNXC:CommandCenter 2026-06-17-09:00: Merging main reconciled the curated count to 23 (main's 22 lazy views/modals + Command Center). + +FNXC:CommandCenter 2026-06-19-00:00: +FN-6702 removes ReliabilityView from the App-level lazy inventory because Reliability now mounts inside the lazy CommandCenter chunk. */ import { describe, expect, it } from "vitest"; import { readFileSync } from "node:fs"; @@ -21,7 +24,6 @@ const EXPECTED_DOCUMENTED_VIEWS = new Set([ "DocumentsView", "SkillsView", "ResearchView", - "ReliabilityView", "CommandCenter", "EvalsView", "TodoView", @@ -47,7 +49,6 @@ const EXPECTED_APP_LEVEL_VIEWS = new Set([ "SkillsView", "MemoryView", "SecretsView", - "ReliabilityView", "CommandCenter", "DevServerView", "TodoView", @@ -105,7 +106,7 @@ function extractAppModalsLazyViews(appModalsSource: string): Set { } describe("AGENTS lazy-loaded views inventory", () => { - it("documents the App-level and AppModals lazy views accurately and keeps the curated 23-view list in sync", () => { + it("documents the App-level and AppModals lazy views accurately and keeps the curated 22-view list in sync", () => { const agentsDoc = readFileSync(resolve(__dirname, "../../../../AGENTS.md"), "utf-8"); const appSource = readFileSync(resolve(__dirname, "../App.tsx"), "utf-8"); const appModalsSource = readFileSync(resolve(__dirname, "../components/AppModals.tsx"), "utf-8"); @@ -113,11 +114,11 @@ describe("AGENTS lazy-loaded views inventory", () => { const section = extractLazyLoadedSection(agentsDoc); const countMatch = section.match(/These\s+(\d+)\s+views\s+are lazy-loaded/); expect(countMatch).toBeTruthy(); - expect(Number(countMatch?.[1])).toBe(23); + expect(Number(countMatch?.[1])).toBe(22); const documentedViews = extractBacktickedNamesFromBullets(section); expect(new Set(documentedViews)).toEqual(EXPECTED_DOCUMENTED_VIEWS); - expect(documentedViews).toHaveLength(23); + expect(documentedViews).toHaveLength(22); expect(section).toContain("`ResearchView`"); expect(section).toContain("`TodoView`"); diff --git a/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx b/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx index 6a56028d42..c3f98cf858 100644 --- a/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx +++ b/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx @@ -159,28 +159,21 @@ describe("Mobile Feature Access Regression Guard", () => { expect(screen.getByTestId("mobile-more-item-schedules")).toBeDefined(); expect(screen.getByTestId("mobile-more-item-github")).toBeDefined(); expect(screen.getByTestId("mobile-more-item-usage")).toBeDefined(); - expect(screen.getByTestId("mobile-more-item-reliability")).toBeDefined(); + expect(screen.queryByTestId("mobile-more-item-reliability")).toBeNull(); expect(screen.queryByTestId("mobile-more-item-chat")).toBeNull(); expect(screen.queryByTestId("mobile-more-item-nodes")).toBeNull(); expect(screen.getByTestId("mobile-more-item-settings")).toBeDefined(); }); - it("reliability view is reachable from mobile More sheet", () => { + it("reliability is no longer a mobile More item and is reached via Command Center", () => { const props = createDefaultMobileNavProps(); render(); fireEvent.click(screen.getByTestId("mobile-nav-tab-more")); + expect(screen.queryByTestId("mobile-more-item-reliability")).toBeNull(); - const reliabilityItem = screen.getByTestId("mobile-more-item-reliability"); - expect(reliabilityItem).toBeDefined(); - fireEvent.click(reliabilityItem); - expect(props.onChangeView).toHaveBeenCalledWith("reliability"); - }); - - it("more tab is active when reliability view is open", () => { - render(); - - expect(screen.getByTestId("mobile-nav-tab-more").className).toContain("mobile-nav-tab--active"); + fireEvent.click(screen.getByTestId("mobile-nav-tab-command-center")); + expect(props.onChangeView).toHaveBeenCalledWith("command-center"); }); it("nodes view is reachable from mobile More sheet when enabled", () => { diff --git a/packages/dashboard/app/components/Header.tsx b/packages/dashboard/app/components/Header.tsx index c555ef4f6e..16dc605205 100644 --- a/packages/dashboard/app/components/Header.tsx +++ b/packages/dashboard/app/components/Header.tsx @@ -1110,7 +1110,7 @@ export function Header({ <> - {isTablet && ( - - {experimentalFeatures?.evalsView && (