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
This commit is contained in:
@@ -413,6 +413,7 @@ describe("runTaskPlan", () => {
|
||||
description: "A well-planned task",
|
||||
column: "triage",
|
||||
dependencies: ["FN-001"],
|
||||
source: { sourceType: "cli" },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ describe("project-aware task command behavior", () => {
|
||||
await runTaskCreate("test task", undefined, undefined, "demo-project");
|
||||
|
||||
expect(resolveProject).toHaveBeenCalledWith("demo-project");
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({ description: "test task", dependencies: undefined });
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({ description: "test task", dependencies: undefined, source: { sourceType: "cli" } });
|
||||
expect(logSpy.mock.calls.some((call) => String(call[0]).includes("Project: demo-project"))).toBe(true);
|
||||
|
||||
logSpy.mockRestore();
|
||||
@@ -428,7 +428,7 @@ describe("project-aware task command behavior", () => {
|
||||
await runTaskCreate("default task");
|
||||
|
||||
expect(resolveProject).toHaveBeenCalledWith(undefined);
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({ description: "default task", dependencies: undefined });
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({ description: "default task", dependencies: undefined, source: { sourceType: "cli" } });
|
||||
});
|
||||
|
||||
it("runTaskCreate without project flag falls back to TaskStore(process.cwd()) when resolution fails", async () => {
|
||||
@@ -452,7 +452,7 @@ describe("project-aware task command behavior", () => {
|
||||
expect(resolveProject).toHaveBeenCalledWith(undefined);
|
||||
expect(TaskStore).toHaveBeenCalledWith("/current/project");
|
||||
expect(init).toHaveBeenCalledOnce();
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({ description: "local task", dependencies: undefined });
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({ description: "local task", dependencies: undefined, source: { sourceType: "cli" } });
|
||||
cwdSpy.mockRestore();
|
||||
});
|
||||
|
||||
@@ -967,6 +967,7 @@ describe("runTaskCreate with --depends", () => {
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({
|
||||
description: "test task",
|
||||
dependencies: ["FN-124"],
|
||||
source: { sourceType: "cli" },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -976,6 +977,7 @@ describe("runTaskCreate with --depends", () => {
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({
|
||||
description: "test task",
|
||||
dependencies: ["FN-124", "FN-100"],
|
||||
source: { sourceType: "cli" },
|
||||
});
|
||||
|
||||
const depsLine = logSpy.mock.calls.find(
|
||||
@@ -992,6 +994,7 @@ describe("runTaskCreate with --depends", () => {
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({
|
||||
description: "test task",
|
||||
dependencies: undefined,
|
||||
source: { sourceType: "cli" },
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1076,6 +1079,7 @@ describe("runTaskImportGitHubInteractive", () => {
|
||||
issueNumber: 1,
|
||||
url: "https://github.com/owner/repo/issues/1",
|
||||
},
|
||||
source: { sourceType: "github_import", sourceMetadata: { issueUrl: "https://github.com/owner/repo/issues/1" } },
|
||||
});
|
||||
expect(mockCreateTask).toHaveBeenCalledWith({
|
||||
title: "Third Issue",
|
||||
@@ -1089,6 +1093,7 @@ describe("runTaskImportGitHubInteractive", () => {
|
||||
issueNumber: 3,
|
||||
url: "https://github.com/owner/repo/issues/3",
|
||||
},
|
||||
source: { sourceType: "github_import", sourceMetadata: { issueUrl: "https://github.com/owner/repo/issues/3" } },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1146,6 +1151,7 @@ describe("runTaskImportGitHubInteractive", () => {
|
||||
issueNumber: 2,
|
||||
url: "https://github.com/owner/repo/issues/2",
|
||||
},
|
||||
source: { sourceType: "github_import", sourceMetadata: { issueUrl: "https://github.com/owner/repo/issues/2" } },
|
||||
});
|
||||
|
||||
const skipLine = logSpy.mock.calls.find(
|
||||
@@ -1399,6 +1405,7 @@ describe("runTaskImportFromGitHub", () => {
|
||||
issueNumber: 1,
|
||||
url: "https://github.com/owner/repo/issues/1",
|
||||
},
|
||||
source: { sourceType: "github_import", sourceMetadata: { issueUrl: "https://github.com/owner/repo/issues/1" } },
|
||||
});
|
||||
|
||||
const successLine = logSpy.mock.calls.find(
|
||||
@@ -1481,6 +1488,7 @@ describe("runTaskImportFromGitHub", () => {
|
||||
issueNumber: 1,
|
||||
url: "https://github.com/owner/repo/issues/1",
|
||||
},
|
||||
source: { sourceType: "github_import", sourceMetadata: { issueUrl: "https://github.com/owner/repo/issues/1" } },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1502,6 +1510,7 @@ describe("runTaskImportFromGitHub", () => {
|
||||
issueNumber: 1,
|
||||
url: "https://github.com/owner/repo/issues/1",
|
||||
},
|
||||
source: { sourceType: "github_import", sourceMetadata: { issueUrl: "https://github.com/owner/repo/issues/1" } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -134,7 +134,11 @@ export async function runTaskCreate(descriptionArg?: string, attachFiles?: strin
|
||||
}
|
||||
|
||||
const store = projectContext?.store ?? await getStore(projectName);
|
||||
const task = await store.createTask({ description: description.trim(), dependencies: depends });
|
||||
const task = await store.createTask({
|
||||
description: description.trim(),
|
||||
dependencies: depends,
|
||||
source: { sourceType: "cli" },
|
||||
});
|
||||
|
||||
let resolvedNode: { id: string; name?: string } | undefined;
|
||||
if (nodeName) {
|
||||
@@ -921,6 +925,10 @@ export async function runTaskImportGitHubInteractive(
|
||||
issueNumber: issue.number,
|
||||
url: issue.html_url,
|
||||
},
|
||||
source: {
|
||||
sourceType: "github_import",
|
||||
sourceMetadata: { issueUrl: issue.html_url },
|
||||
},
|
||||
});
|
||||
|
||||
const label = task.title || task.description.slice(0, 60) + (task.description.length > 60 ? "…" : "");
|
||||
@@ -1064,6 +1072,10 @@ export async function runTaskImportFromGitHub(
|
||||
issueNumber: issue.number,
|
||||
url: issue.html_url,
|
||||
},
|
||||
source: {
|
||||
sourceType: "github_import",
|
||||
sourceMetadata: { issueUrl: issue.html_url },
|
||||
},
|
||||
});
|
||||
|
||||
const label = task.title || task.description.slice(0, 60) + (task.description.length > 60 ? "…" : "");
|
||||
@@ -1637,6 +1649,7 @@ export async function runTaskPlan(initialPlanArg?: string, yesFlag = false, proj
|
||||
description: result.data.description,
|
||||
column: "triage",
|
||||
dependencies: result.data.suggestedDependencies,
|
||||
source: { sourceType: "cli" },
|
||||
});
|
||||
|
||||
console.log();
|
||||
|
||||
@@ -207,6 +207,7 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
description: params.description.trim(),
|
||||
dependencies: params.depends,
|
||||
assignedAgentId: params.agentId,
|
||||
source: { sourceType: "api" },
|
||||
});
|
||||
|
||||
const label =
|
||||
@@ -878,6 +879,10 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
issueNumber: issue.number,
|
||||
url: issue.html_url,
|
||||
},
|
||||
source: {
|
||||
sourceType: "github_import",
|
||||
sourceMetadata: { issueUrl: issue.html_url },
|
||||
},
|
||||
});
|
||||
|
||||
await store.logEntry(task.id, "Imported from GitHub", sourceUrl);
|
||||
@@ -971,6 +976,10 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
issueNumber: issue.number,
|
||||
url: issue.html_url,
|
||||
},
|
||||
source: {
|
||||
sourceType: "github_import",
|
||||
sourceMetadata: { issueUrl: issue.html_url },
|
||||
},
|
||||
});
|
||||
|
||||
await store.logEntry(task.id, "Imported from GitHub", sourceUrl);
|
||||
|
||||
Reference in New Issue
Block a user