feat(KB-334): change default task prefix from KB to FN

- Change default taskPrefix from KB to FN and branch naming from kb/ to fusion/
- Update all test expectations and patterns for new naming convention
- Update settings UI placeholder text to reflect FN prefix
- Update dashboard, engine, and CLI components for fusion/ branch paths
- Add changeset documenting the breaking change for users
This commit is contained in:
gsxdsm
2026-03-31 19:50:15 -07:00
parent ada014213a
commit e98cb5c100
10 changed files with 30 additions and 20 deletions

View File

@@ -0,0 +1,8 @@
---
"@gsxdsm/fusion": minor
"@fusion/core": minor
"@fusion/dashboard": minor
"@fusion/engine": minor
---
Rename default task ID prefix from KB to FN and branch naming from kb/ to fusion/

View File

@@ -153,7 +153,7 @@ describe("kb pi extension", () => {
expect(result.details.taskId).toBe("FN-002"); expect(result.details.taskId).toBe("FN-002");
expect(result.details.dependencies).toEqual(["FN-001"]); expect(result.details.dependencies).toEqual(["FN-001"]);
expect(result.content[0].text).toContain("Dependencies: KB-001"); expect(result.content[0].text).toContain("Dependencies: FN-001");
}); });
}); });
@@ -315,7 +315,7 @@ describe("kb pi extension", () => {
makeCtx(tmpDir), makeCtx(tmpDir),
); );
expect(result.content[0].text).toContain("Attached to KB-001"); expect(result.content[0].text).toContain("Attached to FN-001");
expect(result.content[0].text).toContain("test.txt"); expect(result.content[0].text).toContain("test.txt");
expect(result.details.attachment).toBeDefined(); expect(result.details.attachment).toBeDefined();
expect(result.details.attachment.originalName).toBe("test.txt"); expect(result.details.attachment.originalName).toBe("test.txt");
@@ -366,7 +366,7 @@ describe("kb pi extension", () => {
undefined, undefined,
makeCtx(tmpDir), makeCtx(tmpDir),
); );
expect(pauseResult.content[0].text).toContain("Paused KB-001"); expect(pauseResult.content[0].text).toContain("Paused FN-001");
// Verify it's paused // Verify it's paused
const showTool = api.tools.get("kb_task_show")!; const showTool = api.tools.get("kb_task_show")!;
@@ -388,7 +388,7 @@ describe("kb pi extension", () => {
undefined, undefined,
makeCtx(tmpDir), makeCtx(tmpDir),
); );
expect(unpauseResult.content[0].text).toContain("Unpaused KB-001"); expect(unpauseResult.content[0].text).toContain("Unpaused FN-001");
}); });
}); });
}); });

View File

@@ -66,7 +66,7 @@ describe("runTaskSteer", () => {
expect(mockAddSteeringComment).toHaveBeenCalledWith("FN-001", "Focus on error handling", "user"); expect(mockAddSteeringComment).toHaveBeenCalledWith("FN-001", "Focus on error handling", "user");
expect(mockConsoleLog).toHaveBeenCalledWith( expect(mockConsoleLog).toHaveBeenCalledWith(
expect.stringContaining("Steering comment added to KB-001") expect.stringContaining("Steering comment added to FN-001")
); );
}); });

View File

@@ -942,7 +942,7 @@ describe("runTaskRefine", () => {
mockRefineTask = vi.fn().mockResolvedValue({ mockRefineTask = vi.fn().mockResolvedValue({
id: "FN-002", id: "FN-002",
description: "Refinement of KB-001", description: "Refinement of FN-001",
column: "triage", column: "triage",
dependencies: ["FN-001"], dependencies: ["FN-001"],
steps: [], steps: [],
@@ -1132,7 +1132,7 @@ describe("runTaskDelete", () => {
await runTaskDelete("FN-001", false); await runTaskDelete("FN-001", false);
expect(mockRlQuestion).toHaveBeenCalledOnce(); expect(mockRlQuestion).toHaveBeenCalledOnce();
expect(mockRlQuestion).toHaveBeenCalledWith("Are you sure you want to delete KB-001? [y/N] "); expect(mockRlQuestion).toHaveBeenCalledWith("Are you sure you want to delete FN-001? [y/N] ");
expect(mockRlClose).toHaveBeenCalled(); expect(mockRlClose).toHaveBeenCalled();
expect(mockDeleteTask).toHaveBeenCalledOnce(); expect(mockDeleteTask).toHaveBeenCalledOnce();
expect(mockDeleteTask).toHaveBeenCalledWith("FN-001"); expect(mockDeleteTask).toHaveBeenCalledWith("FN-001");
@@ -1227,7 +1227,7 @@ describe("runTaskComment", () => {
const store = (TaskStore as unknown as ReturnType<typeof vi.fn>).mock.results.at(-1)?.value; const store = (TaskStore as unknown as ReturnType<typeof vi.fn>).mock.results.at(-1)?.value;
expect(store.addTaskComment).toHaveBeenCalledWith("FN-001", "Hello", "alice"); expect(store.addTaskComment).toHaveBeenCalledWith("FN-001", "Hello", "alice");
expect(logSpy).toHaveBeenCalledWith(" ✓ Comment added to KB-001"); expect(logSpy).toHaveBeenCalledWith(" ✓ Comment added to FN-001");
}); });
it("lists task comments", async () => { it("lists task comments", async () => {
@@ -1309,7 +1309,7 @@ describe("runTaskRetry", () => {
column: "in-progress" column: "in-progress"
})); }));
await expect(runTaskRetry("FN-001")).rejects.toThrow("Task KB-001 is not failed (status: none)"); await expect(runTaskRetry("FN-001")).rejects.toThrow("Task FN-001 is not failed (status: none)");
}); });
it("throws error with correct status when task has different status", async () => { it("throws error with correct status when task has different status", async () => {
@@ -1319,7 +1319,7 @@ describe("runTaskRetry", () => {
column: "in-progress" column: "in-progress"
})); }));
await expect(runTaskRetry("FN-001")).rejects.toThrow("Task KB-001 is not failed (status: paused)"); await expect(runTaskRetry("FN-001")).rejects.toThrow("Task FN-001 is not failed (status: paused)");
}); });
}); });
@@ -1434,7 +1434,7 @@ describe("runTaskLogs", () => {
await runTaskLogs("FN-001"); await runTaskLogs("FN-001");
expect(logSpy).toHaveBeenCalledWith("No agent logs found for KB-001"); expect(logSpy).toHaveBeenCalledWith("No agent logs found for FN-001");
}); });
it("exits with error when task not found", async () => { it("exits with error when task not found", async () => {
@@ -1937,7 +1937,7 @@ describe("runTaskPrCreate", () => {
it("exits with error when branch has no commits", async () => { it("exits with error when branch has no commits", async () => {
const task = makeInReviewTask(); const task = makeInReviewTask();
mockGetTask.mockResolvedValueOnce(task); mockGetTask.mockResolvedValueOnce(task);
mockCreatePr.mockRejectedValueOnce(new Error("No commits between main and kb/kb-001")); mockCreatePr.mockRejectedValueOnce(new Error("No commits between main and fusion/fn-001"));
const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => { const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {
throw new Error("process.exit"); throw new Error("process.exit");

View File

@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { Database, createDatabase, toJson, toJsonNullable, fromJson } from "./db.js"; import { Database, createDatabase, toJson, toJsonNullable, fromJson } from "./db.js";
import { DEFAULT_PROJECT_SETTINGS } from "./types.js";
import { mkdtempSync, existsSync } from "node:fs"; import { mkdtempSync, existsSync } from "node:fs";
import { join } from "node:path"; import { join } from "node:path";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
@@ -99,7 +100,7 @@ describe("Database", () => {
expect(row).toBeDefined(); expect(row).toBeDefined();
expect(row.nextId).toBe(1); expect(row.nextId).toBe(1);
expect(row.nextWorkflowStepId).toBe(1); expect(row.nextWorkflowStepId).toBe(1);
expect(row.settings).toBe("{}"); expect(row.settings).toBe(JSON.stringify(DEFAULT_PROJECT_SETTINGS));
expect(row.workflowSteps).toBe("[]"); expect(row.workflowSteps).toBe("[]");
expect(row.updatedAt).toBeTruthy(); expect(row.updatedAt).toBeTruthy();
// updatedAt should be a valid ISO timestamp // updatedAt should be a valid ISO timestamp

View File

@@ -11,6 +11,7 @@
import { DatabaseSync } from "node:sqlite"; import { DatabaseSync } from "node:sqlite";
import { join } from "node:path"; import { join } from "node:path";
import { mkdirSync, existsSync } from "node:fs"; import { mkdirSync, existsSync } from "node:fs";
import { DEFAULT_PROJECT_SETTINGS } from "./types.js";
// ── Types ──────────────────────────────────────────────────────────── // ── Types ────────────────────────────────────────────────────────────
@@ -283,10 +284,10 @@ export class Database {
// Run schema migrations // Run schema migrations
this.migrate(); this.migrate();
// Seed config row idempotently // Seed config row idempotently with default settings
const configNow = new Date().toISOString(); const configNow = new Date().toISOString();
this.db.exec( this.db.exec(
`INSERT OR IGNORE INTO config (id, nextId, nextWorkflowStepId, settings, workflowSteps, updatedAt) VALUES (1, 1, 1, '{}', '[]', '${configNow}')`, `INSERT OR IGNORE INTO config (id, nextId, nextWorkflowStepId, settings, workflowSteps, updatedAt) VALUES (1, 1, 1, '${JSON.stringify(DEFAULT_PROJECT_SETTINGS)}', '[]', '${configNow}')`,
); );
} }

View File

@@ -696,7 +696,7 @@ export const DEFAULT_PROJECT_SETTINGS: ProjectSettings = {
worktreeInitCommand: undefined, worktreeInitCommand: undefined,
recycleWorktrees: false, recycleWorktrees: false,
worktreeNaming: "random", worktreeNaming: "random",
taskPrefix: undefined, taskPrefix: "FN",
includeTaskIdInCommit: true, includeTaskIdInCommit: true,
planningProvider: undefined, planningProvider: undefined,
planningModelId: undefined, planningModelId: undefined,

View File

@@ -454,7 +454,7 @@ export function SettingsModal({
<input <input
id="taskPrefix" id="taskPrefix"
type="text" type="text"
placeholder="KB" placeholder="FN"
value={form.taskPrefix || ""} value={form.taskPrefix || ""}
onChange={(e) => { onChange={(e) => {
const val = e.target.value; const val = e.target.value;

View File

@@ -32,7 +32,7 @@ describe("MergeDetails", () => {
deletions: 2, deletions: 2,
mergedAt: "2026-01-01T01:00:00.000Z", mergedAt: "2026-01-01T01:00:00.000Z",
prNumber: 42, prNumber: 42,
mergeCommitMessage: "feat(KB-001): merge kb/kb-001", mergeCommitMessage: "feat(FN-001): merge fusion/fn-001",
mergeConfirmed: true, mergeConfirmed: true,
}, },
})} })}
@@ -44,7 +44,7 @@ describe("MergeDetails", () => {
expect(screen.getByText("5")).toBeTruthy(); expect(screen.getByText("5")).toBeTruthy();
expect(screen.getByText("+10 / -2")).toBeTruthy(); expect(screen.getByText("+10 / -2")).toBeTruthy();
expect(screen.getByText("#42")).toBeTruthy(); expect(screen.getByText("#42")).toBeTruthy();
expect(screen.getByText("feat(KB-001): merge kb/kb-001")).toBeTruthy(); expect(screen.getByText("feat(FN-001): merge fusion/fn-001")).toBeTruthy();
expect(screen.getByText("Merged successfully")).toBeTruthy(); expect(screen.getByText("Merged successfully")).toBeTruthy();
}); });
}); });

View File

@@ -156,7 +156,7 @@ describe("WorktreePool", () => {
(c) => typeof c[0] === "string" && (c[0] as string).includes("checkout -B"), (c) => typeof c[0] === "string" && (c[0] as string).includes("checkout -B"),
); );
expect(checkoutCall).toBeDefined(); expect(checkoutCall).toBeDefined();
expect(checkoutCall![0]).toBe('git checkout -B "fusion/fn-042" kb/kb-041'); expect(checkoutCall![0]).toBe('git checkout -B "fusion/fn-042" fusion/fn-041');
}); });
it("tolerates git checkout -- . failure (already clean)", () => { it("tolerates git checkout -- . failure (already clean)", () => {