From e22401995eecad3d62a0be7921dc8e9fa814c27a Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 3 Jun 2026 15:40:53 -0700 Subject: [PATCH] Address PR review feedback (#1362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace dynamic await import("@fusion/engine") in the agent-import route with a static top-level import. The dynamic form is banned by the FN-3049 engine-import-regression test (bundler safety); my earlier reply mistook the file's @fusion/core dynamic-import convention for a uniform rule — the regression only forbids @fusion/engine. Verified the test now passes. - AgentImportModal: capture and render dry-run `warnings` in the preview step so the custom-role safeguard is shown BEFORE the import runs, not only after. Adds a regression test for the preview warning. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../app/components/AgentImportModal.tsx | 15 ++++++++++ .../__tests__/AgentImportModal.test.tsx | 30 +++++++++++++++++++ ...r-agent-import-export-generation-routes.ts | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/dashboard/app/components/AgentImportModal.tsx b/packages/dashboard/app/components/AgentImportModal.tsx index a4a12ae6a4..42cc1a0f32 100644 --- a/packages/dashboard/app/components/AgentImportModal.tsx +++ b/packages/dashboard/app/components/AgentImportModal.tsx @@ -137,6 +137,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi const [companyName, setCompanyName] = useState("Unknown"); const [agents, setAgents] = useState([]); const [skills, setSkills] = useState([]); + const [previewWarnings, setPreviewWarnings] = useState([]); const [selectedAgentNames, setSelectedAgentNames] = useState([]); const [selectedSkillNames, setSelectedSkillNames] = useState([]); const [isParsing, setIsParsing] = useState(false); @@ -215,6 +216,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi setCompanyName("Unknown"); setAgents([]); setSkills([]); + setPreviewWarnings([]); setSelectedAgentNames([]); setSelectedSkillNames([]); setIsParsing(false); @@ -345,6 +347,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi created: string[]; skipped: string[]; errors: Array<{ name: string; error: string }>; + warnings?: string[]; }; const previewAgents = (data.agents && data.agents.length > 0) @@ -355,6 +358,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi setCompanyName(data.companyName ?? "Unknown"); setAgents(previewAgents); setSkills(previewSkills); + setPreviewWarnings(Array.isArray(data.warnings) ? data.warnings : []); setSelectedAgentNames(previewAgents.map((agent) => agent.name)); setSelectedSkillNames(previewSkills.map((skill) => skill.name)); setStep("preview"); @@ -678,6 +682,17 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi {companyName} + {previewWarnings.length > 0 && ( +
+ {previewWarnings.map((warning, idx) => ( +
+ + {warning} +
+ ))} +
+ )} +
{agents.length} agent{agents.length !== 1 ? "s" : ""} found diff --git a/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx b/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx index 9922f5c06a..5958c61896 100644 --- a/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx +++ b/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx @@ -110,6 +110,36 @@ describe("AgentImportModal", () => { }); }); + it("surfaces dry-run warnings in the preview step, before import (issue #1261)", async () => { + vi.mocked(globalThis.fetch).mockImplementationOnce(() => mockFetchResponse({ + ok: true, + status: 200, + body: { + dryRun: true, + companyName: "Acme Co", + agents: [{ name: "CEO", role: "custom" }], + created: ["CEO"], + skipped: [], + errors: [], + warnings: [ + "1 imported agent(s) have role \"custom\" and won't be auto-assigned mission or queue work.", + ], + }, + })); + + render(); + + fireEvent.change(screen.getByLabelText("Manifest content"), { + target: { value: "---\nname: CEO\n---\nLead" }, + }); + + fireEvent.click(screen.getByRole("button", { name: "Preview" })); + + await waitFor(() => { + expect(screen.getByText(/won't be auto-assigned mission or queue work/)).toBeTruthy(); + }); + }); + it("imports agents from preview step and shows result summary", async () => { vi.mocked(globalThis.fetch) .mockImplementationOnce(() => mockFetchResponse({ diff --git a/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts b/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts index 8d4317b762..ccec071bf4 100644 --- a/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts +++ b/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts @@ -4,6 +4,7 @@ import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { Readable } from "node:stream"; import { pipeline as streamPipeline } from "node:stream/promises"; +import { listEligibleExecutorAgents } from "@fusion/engine"; import { ApiError, badRequest, notFound, rateLimited } from "../api-error.js"; import { createSessionDiagnostics } from "../ai-session-diagnostics.js"; import { writeSSEEvent } from "../sse-buffer.js"; @@ -741,7 +742,6 @@ async function persistImportedSkills( const customRoleCount = importItems.filter((item) => item.input.role === "custom").length; const importsAnExecutor = importItems.some((item) => item.input.role === "executor"); if (customRoleCount > 0 && !importsAnExecutor) { - const { listEligibleExecutorAgents } = await import("@fusion/engine"); const existingExecutors = await listEligibleExecutorAgents(agentStore).catch(() => []); if (existingExecutors.length === 0) { importWarnings.push(