FN-7093: tighten lazy-view inventory guard
Keep the lazy-loaded views documentation guard aligned with every curated chunk source. - Expand AGENTS guidance to name the plugin and agent detail lazy-import sources. - Teach the dashboard docs test to scan feature-owned lazy declarations in PluginsSection and AgentsView. - Assert explicit exclusions for embedded views, terminal/onboarding internals, and duplicate overflow imports. Files changed: AGENTS.md | 4 +- .../app/__tests__/lazy-loaded-views-docs.test.ts | 113 +++++++++++++++++++-- 2 files changed, 108 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-7093 Fusion-Task-Lineage: 25bbe920-529d-4f2b-95d4-4c17b682dc49 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -259,7 +259,7 @@ Scoped exception (FN-5819): shared-branch-group members (`branchContext.assignme
|
||||
### Lazy-Loaded Heavy Views
|
||||
|
||||
These 20 views are lazy-loaded via `React.lazy()` with `<Suspense fallback={null}>`.
|
||||
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`.
|
||||
Keep this AGENTS inventory in sync with App lazy imports, AppModals lazy modal imports (`SettingsModal`, `WorkflowNodeEditor`, `SetupWizardModal`), plugin settings lazy imports (`PluginManager`, `PiExtensionsManager`), AgentsView lazy imports (`AgentDetailView`), and `packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts`.
|
||||
|
||||
- `AgentsView`
|
||||
- `ChatView`
|
||||
@@ -282,7 +282,7 @@ Keep this AGENTS inventory in sync with App lazy imports, AppModals lazy modal i
|
||||
- `PiExtensionsManager`
|
||||
- `AgentDetailView`
|
||||
|
||||
Note: the embedded main-content views Workflows (`_WorkflowEditorView`), Import Tasks (`_ImportTasksView`), Automations (`_AutomationsView`), and Settings (`_SettingsView`) in App.tsx are `_`-prefixed lazy splits that reuse already-documented chunks. They are intentionally excluded from the curated list above and from the count; `lazy-loaded-views-docs.test.ts` filters out `_`-prefixed lazy consts (`extractAppLazyViews`), so do not add them as bullets.
|
||||
Note: the embedded main-content views Workflows (`_WorkflowEditorView`), Import Tasks (`_ImportTasksView`), Automations (`_AutomationsView`), and Settings (`_SettingsView`) in App.tsx are `_`-prefixed lazy splits that reuse already-documented chunks. Task session terminals, onboarding-internal modals, duplicate `AgentDetailView` imports, and right-dock overflow re-imports of already-counted views are also intentionally excluded. These exclusions stay out of the curated list and count; `lazy-loaded-views-docs.test.ts` asserts them explicitly, so do not add them as bullets.
|
||||
|
||||
## FNXC_LOG comments:
|
||||
- Please whenever you're working on a codebase. I want you to add comments describing the date of the change (must be in this format yyyy-MM-dd-hh:mm) and describing the requirements or the change in requirements that made you implement certain functionality.
|
||||
|
||||
@@ -17,6 +17,12 @@ FN-6881 removes StashRecoveryView from the App-level lazy inventory because Stas
|
||||
|
||||
FNXC:DashboardLazyViews 2026-06-22-00:00:
|
||||
The navigation reshuffle promotes Workflows, Import Tasks, Automations, and Settings as embedded views that reuse existing lazy chunks. Their underscore-prefixed App consts stay out of the curated inventory so the docs count each heavy chunk once.
|
||||
|
||||
FNXC:DashboardLazyViews 2026-06-26-00:00:
|
||||
Curated chunks declared outside App.tsx/AppModals.tsx (Plugins settings section, AgentsView) must be scanned at their real call sites so the inventory cannot drift while this guard stays green.
|
||||
|
||||
FNXC:DashboardLazyViews 2026-06-26-00:00:
|
||||
Session terminals, onboarding internals, and overflow re-imports of already-counted chunks are explicit exclusions because they are not new heavy top-level views or modals for the AGENTS inventory.
|
||||
*/
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
@@ -72,6 +78,57 @@ const EXPECTED_APP_MODALS_LAZY_VIEWS = new Set([
|
||||
"WorkflowNodeEditor",
|
||||
]);
|
||||
|
||||
const EXPECTED_PLUGINS_SECTION_LAZY_VIEWS = new Set([
|
||||
"PluginManager",
|
||||
"PiExtensionsManager",
|
||||
]);
|
||||
|
||||
const EXPECTED_AGENTS_VIEW_LAZY_VIEWS = new Set([
|
||||
"AgentDetailView",
|
||||
]);
|
||||
|
||||
const EXPECTED_EXCLUDED_LAZY = [
|
||||
{
|
||||
file: "../App.tsx",
|
||||
symbols: ["_WorkflowEditorView", "_ImportTasksView", "_AutomationsView", "_SettingsView"],
|
||||
reason: "embedded App presentations reuse already-documented modal/import chunks",
|
||||
},
|
||||
{
|
||||
file: "../components/TaskDetailModal.tsx",
|
||||
symbols: ["LazySessionTerminal"],
|
||||
reason: "task session terminal, not a top-level heavy view or modal",
|
||||
},
|
||||
{
|
||||
file: "../components/TaskDetailModal.tsx",
|
||||
symbols: ["AgentDetailView"],
|
||||
reason: "re-import of the same AgentDetailView chunk curated from AgentsView.tsx",
|
||||
countedBy: "../components/AgentsView.tsx",
|
||||
},
|
||||
{
|
||||
file: "../components/ModelOnboardingModal.tsx",
|
||||
symbols: ["ExperimentalAgentOnboardingModal"],
|
||||
reason: "onboarding internal loaded inside the existing model onboarding flow",
|
||||
},
|
||||
{
|
||||
file: "../components/SetupWizardModal.tsx",
|
||||
symbols: ["ExperimentalAgentOnboardingModal"],
|
||||
reason: "onboarding internal loaded inside the existing setup wizard flow",
|
||||
},
|
||||
{
|
||||
file: "../components/overflowViewRegistry.tsx",
|
||||
symbols: ["DevServerView", "SecretsView", "TodoView", "PullRequestView"],
|
||||
reason: "right-dock overflow re-imports of App-level chunks already counted once",
|
||||
countedBy: "../App.tsx",
|
||||
},
|
||||
] as const;
|
||||
|
||||
const EXPECTED_CURATED_LAZY_SOURCES = [
|
||||
{ file: "../App.tsx", symbols: EXPECTED_APP_LEVEL_VIEWS },
|
||||
{ file: "../components/AppModals.tsx", symbols: EXPECTED_APP_MODALS_LAZY_VIEWS },
|
||||
{ file: "../components/settings/sections/PluginsSection.tsx", symbols: EXPECTED_PLUGINS_SECTION_LAZY_VIEWS },
|
||||
{ file: "../components/AgentsView.tsx", symbols: EXPECTED_AGENTS_VIEW_LAZY_VIEWS },
|
||||
] as const;
|
||||
|
||||
function extractLazyLoadedSection(agentsDoc: string): string {
|
||||
const match = agentsDoc.match(/### Lazy-Loaded Heavy Views[\s\S]*?(?=\n### |\n---|$)/);
|
||||
if (!match) {
|
||||
@@ -102,11 +159,20 @@ function extractAppModalsLazyViews(appModalsSource: string): Set<string> {
|
||||
return new Set(extractConstLazyViews(appModalsSource));
|
||||
}
|
||||
|
||||
function expectDocumentedViews(include: Iterable<string>, section: string): void {
|
||||
for (const view of include) {
|
||||
expect(EXPECTED_DOCUMENTED_VIEWS.has(view)).toBe(true);
|
||||
expect(section).toContain(`\`${view}\``);
|
||||
}
|
||||
}
|
||||
|
||||
describe("AGENTS lazy-loaded views inventory", () => {
|
||||
it("documents the App-level and AppModals lazy views accurately and keeps the curated 20-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");
|
||||
const pluginsSectionSource = readFileSync(resolve(__dirname, "../components/settings/sections/PluginsSection.tsx"), "utf-8");
|
||||
const agentsViewSource = readFileSync(resolve(__dirname, "../components/AgentsView.tsx"), "utf-8");
|
||||
|
||||
const section = extractLazyLoadedSection(agentsDoc);
|
||||
const countMatch = section.match(/These\s+(\d+)\s+views\s+are lazy-loaded/);
|
||||
@@ -123,21 +189,54 @@ describe("AGENTS lazy-loaded views inventory", () => {
|
||||
expect(section).toContain("`WorkflowNodeEditor`");
|
||||
expect(section).toContain("`_ImportTasksView`");
|
||||
expect(section).toContain("`_AutomationsView`");
|
||||
expect((section.match(/`AgentDetailView`/g) ?? []).length).toBe(1);
|
||||
expect(documentedViews.filter((view) => view === "AgentDetailView")).toHaveLength(1);
|
||||
|
||||
const appLevelViews = extractAppLazyViews(appSource);
|
||||
expect(appLevelViews).toEqual(EXPECTED_APP_LEVEL_VIEWS);
|
||||
|
||||
for (const view of appLevelViews) {
|
||||
expect(EXPECTED_DOCUMENTED_VIEWS.has(view)).toBe(true);
|
||||
}
|
||||
expectDocumentedViews(appLevelViews, section);
|
||||
|
||||
const appModalsLazyViews = extractAppModalsLazyViews(appModalsSource);
|
||||
expect(appModalsLazyViews).toEqual(EXPECTED_APP_MODALS_LAZY_VIEWS);
|
||||
expectDocumentedViews(appModalsLazyViews, section);
|
||||
|
||||
for (const view of appModalsLazyViews) {
|
||||
expect(EXPECTED_DOCUMENTED_VIEWS.has(view)).toBe(true);
|
||||
expect(section).toContain(`\`${view}\``);
|
||||
/*
|
||||
* FNXC:DashboardLazyViews 2026-06-26-00:00:
|
||||
* Plugin manager chunks and the agent detail chunk are curated AGENTS entries, but their lazy declarations live in feature-owned source files rather than App.tsx/AppModals.tsx. Scan those real call sites so removing or renaming one of these declarations fails this inventory guard.
|
||||
*/
|
||||
const pluginsSectionLazyViews = new Set(extractConstLazyViews(pluginsSectionSource));
|
||||
expect(pluginsSectionLazyViews).toEqual(EXPECTED_PLUGINS_SECTION_LAZY_VIEWS);
|
||||
expectDocumentedViews(pluginsSectionLazyViews, section);
|
||||
|
||||
const agentsViewLazyViews = new Set(extractConstLazyViews(agentsViewSource));
|
||||
expect(agentsViewLazyViews).toEqual(EXPECTED_AGENTS_VIEW_LAZY_VIEWS);
|
||||
expectDocumentedViews(agentsViewLazyViews, section);
|
||||
|
||||
/*
|
||||
* FNXC:DashboardLazyViews 2026-06-26-00:00:
|
||||
* These lazy consts are intentionally outside the curated 20: task session terminals, onboarding internals, duplicate AgentDetail/overflow imports, and underscore-prefixed embedded views do not create additional top-level heavy inventory entries.
|
||||
*/
|
||||
for (const exclusion of EXPECTED_EXCLUDED_LAZY) {
|
||||
const excludedSource = readFileSync(resolve(__dirname, exclusion.file), "utf-8");
|
||||
const excludedLazyViews = new Set(extractConstLazyViews(excludedSource));
|
||||
for (const symbol of exclusion.symbols) {
|
||||
expect(excludedLazyViews.has(symbol), exclusion.reason).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
const curatedSourceKeys = new Set(
|
||||
EXPECTED_CURATED_LAZY_SOURCES.flatMap((source) => [...source.symbols].map((symbol) => `${source.file}:${symbol}`)),
|
||||
);
|
||||
for (const exclusion of EXPECTED_EXCLUDED_LAZY) {
|
||||
for (const symbol of exclusion.symbols) {
|
||||
expect(curatedSourceKeys.has(`${exclusion.file}:${symbol}`), exclusion.reason).toBe(false);
|
||||
if ("countedBy" in exclusion) {
|
||||
expect(curatedSourceKeys.has(`${exclusion.countedBy}:${symbol}`), exclusion.reason).toBe(true);
|
||||
expect(documentedViews.filter((view) => view === symbol), exclusion.reason).toHaveLength(1);
|
||||
} else {
|
||||
expect(EXPECTED_DOCUMENTED_VIEWS.has(symbol), exclusion.reason).toBe(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user