Files
fusion/packages/dashboard/app/hooks/__tests__/useTaskHandlers.test.ts
Fusion b0cda2398b feat(FN-2923): merge fusion/fn-2923
- Add task `source` provenance field tracking how tasks enter the system (e.g. `"manual"`, `"heartbeat"`, `"executor"`, `"triage"`, `"cron"`, `"routine"`)
- Wire `source` into shared create-task tools, executor agent, triage agent, cron runner, and routine runner
- Add `source` to heartbeat task creation in `agent-heartbeat.ts` and mission execution loop in `mission-execution-loop.ts`
- Wire `source` field into dashboard routes (`register-planning-subtask-routes.ts`, `register-task-workflow-routes.ts`) and GitHub integration (`register-git-github.ts`)
- Add `source` to the API layer and task store handlers in the dashboard
- Expose `source` in the `fn` CLI task command and `task-plan` extension tool
- Add integration tests for provenance across engine (agent-tools, agent-heartbeat, cron-runner, triage, pr-comment-handler) and dashboard routes
- Update existing tests to assert the correct source values
- Bulk-revert stale changeset files that were superseded by the v0.9.1 release commit

Commits merged:
- test(FN-2923): complete Step 14 — align tests with task source provenance
- test(FN-2923): complete Steps 12-13 — update and add provenance tests
- feat(FN-2923): complete Step 10 — wire CLI task provenance
- feat(FN-2923): complete Step 9 — wire dashboard UI and API source fields
- feat(FN-2923): complete Step 8 — wire dashboard route provenance
- feat(FN-2923): complete Step 7 — wire remaining engine provenance
- feat(FN-2923): complete Step 6 — add routine create-task provenance
- feat(FN-2923): complete Step 5 — add cron create-task provenance
- feat(FN-2923): complete Step 4 — add triage subtask source
- feat(FN-2923): complete Step 3 — add executor create-task source
- feat(FN-2923): complete Step 2 — add heartbeat task provenance
- feat(FN-2923): complete Step 1 — add provenance to shared create-task tools
- feat(FN-2945): merge fusion/fn-2945
- chore(release): v0.9.1

Files changed:
.changeset/active-agents-no-stuck-connecting.md    | 13 ---
 .changeset/active-agents-panel-hoist-heartbeat.md  | 13 ---
 .changeset/fix-agent-heartbeat-terminal-links.md   |  5 --
 .changeset/fix-experimental-defaults.md            |  5 --
 .changeset/improve-git-manager-diff-pane.md        |  5 --
 .changeset/insights-two-pane-layout.md             |  5 --
 .changeset/show-planning-tasks-immediately.md      |  5 --
 CHANGELOG.md                                       | 92 ++++++++++++++++++++++
 package.json                                       |  2 +-
 packages/cli-alias/CHANGELOG.md                    | 15 ++++
 packages/cli-alias/package.json                    |  2 +-
 packages/cli/CHANGELOG.md                          | 12 +++
 packages/cli/package.json                          |  2 +-
 packages/cli/src/__tests__/task-plan.test.ts       |  1 +
 packages/cli/src/commands/__tests__/task.test.ts   | 15 +++-
 packages/cli/src/commands/task.ts                  | 15 +++-
 packages/cli/src/extension.ts                      |  9 +++
 packages/core/CHANGELOG.md                         |  7 ++
 packages/core/package.json                         |  2 +-
 packages/dashboard/CHANGELOG.md                    | 14 ++++
 .../app/__tests__/agent-css-classes.test.ts        |  3 +-
 packages/dashboard/app/__tests__/api.test.ts       | 13 +++
 packages/dashboard/app/api/legacy.ts               |  2 +
 packages/dashboard/app/components/AgentsView.css   | 22 +++---
 .../app/components/ModelOnboardingModal.tsx        |  5 +-
 packages/dashboard/app/components/TodoView.tsx     |  2 +
 .../__tests__/ModelOnboardingModal.test.tsx        |  4 +-
 .../app/components/__tests__/QuickChatFAB.test.tsx | 14 +---
 .../app/components/__tests__/TodoView.test.tsx     |  4 +-
 .../__tests__/agents-view-mobile.test.tsx          |  5 +-
 .../components/__tests__/onboarding-flow.test.tsx  |  2 +-
 .../app/hooks/__tests__/useTaskHandlers.test.ts    |  4 +-
 packages/dashboard/app/hooks/useTaskHandlers.ts    |  4 +-
 packages/dashboard/package.json                    |  2 +-
 packages/dashboard/src/__tests__/routes.test.ts    | 14 ++++
 packages/dashboard/src/routes.ts                   |  4 +
 .../dashboard/src/routes/register-git-github.ts    | 12 +++
 .../src/routes/register-planning-subtask-routes.ts |  3 +
 .../src/routes/register-task-workflow-routes.ts    |  7 ++
 packages/desktop/CHANGELOG.md                      |  7 ++
 packages/desktop/package.json                      |  2 +-
 packages/engine/CHANGELOG.md                       | 11 +++
 packages/engine/package.json                       |  2 +-
 .../engine/src/__tests__/agent-heartbeat.test.ts   | 10 +++
 .../src/__tests__/agent-tools-delegation.test.ts   |  2 +
 packages/engine/src/__tests__/agent-tools.test.ts  | 37 +++++++++
 packages/engine/src/__tests__/cron-runner.test.ts  |  4 +
 .../src/__tests__/pr-comment-handler.test.ts       |  8 ++
 packages/engine/src/__tests__/triage.test.ts       |  2 +
 packages/engine/src/agent-heartbeat.ts             | 13 ++-
 packages/engine/src/agent-tools.ts                 | 13 ++-
 packages/engine/src/cron-runner.ts                 |  7 +-
 packages/engine/src/executor.ts                    |  2 +-
 packages/engine/src/mission-execution-loop.ts      |  8 ++
 packages/engine/src/pr-comment-handler.ts          |  5 ++
 packages/engine/src/project-engine.ts              |  8 ++
 packages/engine/src/routine-runner.ts              |  4 +
 packages/engine/src/triage.ts                      |  4 +
 packages/mobile/CHANGELOG.md                       |  7 ++
 packages/mobile/package.json                       |  2 +-
 packages/pi-claude-cli/CHANGELOG.md                |  7 ++
 packages/pi-claude-cli/package.json                |  2 +-
 packages/plugin-sdk/CHANGELOG.md                   | 10 +++
 packages/plugin-sdk/package.json                   |  2 +-
 .../examples/fusion-plugin-auto-label/CHANGELOG.md |  8 ++
 .../examples/fusion-plugin-auto-label/package.json |  2 +-
 .../examples/fusion-plugin-ci-status/CHANGELOG.md  |  8 ++
 .../examples/fusion-plugin-ci-status/package.json  |  2 +-
 .../fusion-plugin-notification/CHANGELOG.md        |  8 ++
 .../fusion-plugin-notification/package.json        |  2 +-
 .../fusion-plugin-settings-demo/CHANGELOG.md       |  8 ++
 .../fusion-plugin-settings-demo/package.json       |  2 +-
 plugins/fusion-plugin-hermes-runtime/CHANGELOG.md  |  8 ++
 plugins/fusion-plugin-hermes-runtime/package.json  |  2 +-
 .../fusion-plugin-openclaw-runtime/CHANGELOG.md    |  8 ++
 .../fusion-plugin-openclaw-runtime/package.json    |  2 +-
 .../fusion-plugin-paperclip-runtime/CHANGELOG.md   |  8 ++
 .../fusion-plugin-paperclip-runtime/package.json   |  2 +-
 78 files changed, 499 insertions(+), 115 deletions(-)

Fusion-Task-Id: FN-2923
2026-04-29 17:23:22 -07:00

114 lines
3.7 KiB
TypeScript

import { act, renderHook } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useTaskHandlers } from "../useTaskHandlers";
import type { Task, TaskCreateInput } from "@fusion/core";
const CREATED_TASK: Task = {
id: "FN-123",
title: "Test",
description: "Created task",
status: "pending",
column: "triage",
steps: [],
currentStep: 0,
dependencies: [],
log: [],
attachments: [],
createdAt: "",
updatedAt: "",
size: "M",
reviewLevel: 0,
};
function createOptions(overrides: Partial<Parameters<typeof useTaskHandlers>[0]> = {}): Parameters<typeof useTaskHandlers>[0] {
return {
createTask: vi.fn().mockResolvedValue(CREATED_TASK),
ingestCreatedTasks: vi.fn(),
onPlanningTaskCreated: vi.fn(),
onPlanningTasksCreated: vi.fn(),
onSubtaskTasksCreated: vi.fn(),
addToast: vi.fn(),
...overrides,
};
}
describe("useTaskHandlers", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("handleBoardQuickCreate calls createTask with triage column and returns task", async () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
const input: TaskCreateInput = { description: "Do work" };
let created: Task | null = null;
await act(async () => {
created = await result.current.handleBoardQuickCreate(input);
});
expect(options.createTask).toHaveBeenCalledWith({ description: "Do work", column: "triage", source: { sourceType: "dashboard_ui" } });
expect(created).toEqual(CREATED_TASK);
});
it("handleModalCreate calls createTask with triage column and returns task", async () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
let created: Task | null = null;
await act(async () => {
created = await result.current.handleModalCreate({ description: "From modal" });
});
expect(options.createTask).toHaveBeenCalledWith({ description: "From modal", column: "triage", source: { sourceType: "dashboard_ui" } });
expect(created).toEqual(CREATED_TASK);
});
it("handlePlanningTaskCreated delegates with addToast", () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
act(() => {
result.current.handlePlanningTaskCreated(CREATED_TASK);
});
expect(options.ingestCreatedTasks).toHaveBeenCalledWith([CREATED_TASK]);
expect(options.onPlanningTaskCreated).toHaveBeenCalledWith(CREATED_TASK, options.addToast);
});
it("handlePlanningTasksCreated delegates with addToast", () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
act(() => {
result.current.handlePlanningTasksCreated([CREATED_TASK]);
});
expect(options.ingestCreatedTasks).toHaveBeenCalledWith([CREATED_TASK]);
expect(options.onPlanningTasksCreated).toHaveBeenCalledWith([CREATED_TASK], options.addToast);
});
it("handleSubtaskTasksCreated delegates with addToast", () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
act(() => {
result.current.handleSubtaskTasksCreated([CREATED_TASK]);
});
expect(options.ingestCreatedTasks).toHaveBeenCalledWith([CREATED_TASK]);
expect(options.onSubtaskTasksCreated).toHaveBeenCalledWith([CREATED_TASK], options.addToast);
});
it("handleGitHubImport shows success toast with task ID", () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
act(() => {
result.current.handleGitHubImport(CREATED_TASK);
});
expect(options.addToast).toHaveBeenCalledWith("Imported FN-123 from GitHub", "success");
});
});