feat(FN-3087): add immediate wake controls for agent inbox and message API,

This merge ships five distinct features: experimental-flag gating for research tools in the CLI extension and core settings, immediate wake controls for the agent inbox and message API enabling on-demand heartbeat triggers, separation of plugin lifecycle from setup probe state in the dashboard, grap

Fusion-Task-Id: FN-3087
This commit is contained in:
Fusion
2026-05-07 10:52:12 -07:00
committed by gsxdsm
parent 30767ba0c4
commit 514e5f3b94
8 changed files with 64 additions and 9 deletions

View File

@@ -100,6 +100,7 @@ describe("DependencyGraph highlighting", () => {
expect(edgeCB?.className.baseVal || edgeCB?.className).toContain("graph-edge--highlighted");
fireEvent.click(screen.getByTestId("task-C"));
expect(onOpenDetail).toHaveBeenCalledTimes(1);
expect(onOpenDetail).toHaveBeenCalledWith(expect.objectContaining({ id: "C" }));
});

View File

@@ -111,10 +111,11 @@ describe("DependencyGraph", () => {
expect(fitToGraph).toHaveBeenCalled();
});
it("clicking a card triggers onOpenDetail", () => {
it("clicking a card triggers onOpenDetail exactly once", () => {
const onOpenDetail = vi.fn();
render(<DependencyGraph tasks={[createTask("A", "in-progress")]} onOpenDetail={onOpenDetail} />);
fireEvent.click(screen.getByTestId("task-A"));
expect(onOpenDetail).toHaveBeenCalledTimes(1);
expect(onOpenDetail).toHaveBeenCalledWith(expect.objectContaining({ id: "A" }));
});

View File

@@ -220,13 +220,26 @@ describe("GraphTaskNode", () => {
expect(screen.getByTestId("graph-task-node-FN-TEST").hasAttribute("data-current-step")).toBe(false);
});
it("clicking card opens task detail", () => {
it("clicking card opens task detail exactly once", () => {
const props = createProps(createTask());
const { container } = render(<GraphTaskNode {...props} />);
const card = container.querySelector(".card");
expect(card).toBeTruthy();
fireEvent.click(card!);
expect(props.onOpenDetail).toHaveBeenCalledTimes(1);
expect(props.onOpenDetail).toHaveBeenCalledWith(expect.objectContaining({ id: "FN-TEST" }));
});
it("clicking active indicator surface opens task detail", () => {
const props = createProps(createTask({ column: "in-progress", status: "executing" }));
const { container } = render(<GraphTaskNode {...props} />);
const indicator = container.querySelector(".graph-task-active-indicator");
expect(indicator).toBeTruthy();
fireEvent.click(indicator!);
expect(props.onOpenDetail).toHaveBeenCalledTimes(1);
expect(props.onOpenDetail).toHaveBeenCalledWith(expect.objectContaining({ id: "FN-TEST" }));
});

View File

@@ -1,9 +1,20 @@
import { describe, expect, it } from "vitest";
import { createElement } from "react";
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { describe, expect, it, vi, afterEach } from "vitest";
import { definePlugin } from "@fusion/plugin-sdk";
import { validatePluginManifest } from "@fusion/core";
import plugin from "../index";
import plugin, { DependencyGraphDashboardView } from "../index";
import { getPluginViewId } from "../../../../packages/dashboard/app/plugins/pluginViewRegistry";
vi.mock("@fusion/dashboard/app/components/TaskCard", () => ({
TaskCard: ({ task, onOpenDetail }: { task: { id: string }; onOpenDetail: (task: { id: string }) => void }) =>
createElement("button", { "data-testid": `task-${task.id}`, onClick: () => onOpenDetail(task) }, task.id),
}));
afterEach(() => {
cleanup();
});
describe("dependency graph plugin host integration contract", () => {
it("declares dashboard view manifest shape", () => {
expect(plugin.manifest.id).toBe("fusion-plugin-dependency-graph");
@@ -43,4 +54,20 @@ describe("dependency graph plugin host integration contract", () => {
expect(getPluginViewId(plugin.manifest.id, view.viewId)).toBe("plugin:fusion-plugin-dependency-graph:graph");
});
it("uses host openTaskDetail context when rendered through dashboard view entrypoint", () => {
const openTaskDetail = vi.fn();
render(
createElement(DependencyGraphDashboardView, {
context: {
tasks: [{ id: "FN-HOST", description: "FN-HOST", column: "todo", dependencies: [], steps: [], currentStep: 0, log: [] }],
openTaskDetail,
} as never,
}),
);
fireEvent.click(screen.getByTestId("task-FN-HOST"));
expect(openTaskDetail).toHaveBeenCalledTimes(1);
expect(openTaskDetail).toHaveBeenCalledWith(expect.objectContaining({ id: "FN-HOST" }));
});
});

View File

@@ -65,6 +65,7 @@ describe("dependency graph interactions", () => {
render(<DependencyGraph tasks={[createTask("A", "in-progress")]} onOpenDetail={onOpenDetail} />);
fireEvent.click(screen.getByTestId("task-A"));
expect(onOpenDetail).toHaveBeenCalledTimes(1);
expect(onOpenDetail).toHaveBeenCalledWith(expect.objectContaining({ id: "A" }));
expect(screen.getAllByText(/Executing/).length).toBeGreaterThan(0);
});