feat(FN-2604): merge fusion/fn-2604 (auto-resolved)

- feat(FN-2604): complete Step 8 — add changeset and delivery docs
- test(FN-2604): complete Step 7 — align tests and regenerate extension skill docs
- test(FN-2604): complete Step 6 — update dashboard tests for planning statuses
- feat(FN-2604): complete Step 5 — update CLI planning terminology
- feat(FN-2604): complete Step 4 — update workflow route status strings
- feat(FN-2604): complete Step 3 — update settings planning terminology
- feat(FN-2604): complete Step 2 — rename replanning actions in column and spec editor
- feat(FN-2604): complete Step 1 — update dashboard component status strings
This commit is contained in:
Fusion
2026-04-26 14:06:48 -07:00
committed by gsxdsm
parent 651ae34307
commit 03a48ae9bb
14 changed files with 97 additions and 92 deletions

View File

@@ -1946,30 +1946,30 @@ describe("POST /tasks/:id/retry", () => {
expect(store.logEntry).toHaveBeenCalledWith("KB-001", "Retry requested from dashboard (stuck kill budget reset)");
});
it("retries a stranded specifying triage task in triage and removes stale prompt", async () => {
it("retries a stranded planning triage task in triage and removes stale prompt", async () => {
const tempRoot = mkdtempSync(join(tmpdir(), "kb-task-retry-spec-"));
const taskDir = join(tempRoot, ".fusion", "tasks", "FN-001");
mkdirSync(taskDir, { recursive: true });
writeFileSync(join(taskDir, "PROMPT.md"), "# stale spec\n");
const specifyingTask = {
const planningTask = {
...FAKE_TASK_DETAIL,
column: "triage" as const,
status: "specifying",
status: "planning",
stuckKillCount: 6,
recoveryRetryCount: 2,
nextRecoveryAt: new Date(Date.now() + 60_000).toISOString(),
};
const retriedTask = {
...specifyingTask,
status: "needs-respecify",
...planningTask,
status: "needs-replan",
stuckKillCount: 0,
recoveryRetryCount: undefined,
nextRecoveryAt: undefined,
};
(store.getTask as ReturnType<typeof vi.fn>)
.mockResolvedValueOnce(specifyingTask)
.mockResolvedValueOnce(planningTask)
.mockResolvedValueOnce(retriedTask);
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue(retriedTask);
(store.getRootDir as ReturnType<typeof vi.fn>).mockReturnValue(tempRoot);
@@ -1981,7 +1981,7 @@ describe("POST /tasks/:id/retry", () => {
expect(res.status).toBe(200);
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
status: "needs-respecify",
status: "needs-replan",
error: null,
worktree: null,
branch: null,
@@ -1995,9 +1995,9 @@ describe("POST /tasks/:id/retry", () => {
expect(existsSync(join(taskDir, "PROMPT.md"))).toBe(false);
expect(store.logEntry).toHaveBeenCalledWith(
"KB-001",
"Retry requested from dashboard (specification retry budget reset)",
"Retry requested from dashboard (planning retry budget reset)",
);
expect(res.body.status).toBe("needs-respecify");
expect(res.body.status).toBe("needs-replan");
} finally {
rmSync(tempRoot, { recursive: true, force: true });
}
@@ -6916,7 +6916,7 @@ describe("POST /tasks/:id/spec/revise", () => {
);
expect(store.moveTask).toHaveBeenCalledWith("FN-001", "triage");
expect(existsSync(join(taskDir, "PROMPT.md"))).toBe(false);
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-respecify" });
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-replan" });
} finally {
rmSync(tempRoot, { recursive: true, force: true });
}
@@ -6944,7 +6944,7 @@ describe("POST /tasks/:id/spec/revise", () => {
it("allows spec revision for task already in triage", async () => {
const triageTask = { ...FAKE_TASK_DETAIL, column: "triage" as const };
const updatedTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "needs-respecify" as const };
const updatedTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "needs-replan" as const };
const tempRoot = mkdtempSync(join(tmpdir(), "kb-spec-revise-triage-"));
const taskDir = join(tempRoot, ".fusion", "tasks", "FN-001");
mkdirSync(taskDir, { recursive: true });
@@ -6973,7 +6973,7 @@ describe("POST /tasks/:id/spec/revise", () => {
);
expect(store.moveTask).not.toHaveBeenCalled();
expect(existsSync(join(taskDir, "PROMPT.md"))).toBe(false);
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-respecify" });
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-replan" });
} finally {
rmSync(tempRoot, { recursive: true, force: true });
}
@@ -7149,7 +7149,7 @@ describe("POST /tasks/:id/spec/rebuild", () => {
);
expect(store.moveTask).toHaveBeenCalledWith("FN-001", "triage");
expect(existsSync(join(taskDir, "PROMPT.md"))).toBe(false);
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-respecify" });
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-replan" });
} finally {
rmSync(tempRoot, { recursive: true, force: true });
}
@@ -7167,7 +7167,7 @@ describe("POST /tasks/:id/spec/rebuild", () => {
expect(res.status).toBe(200);
expect(store.moveTask).toHaveBeenCalledWith("FN-001", "triage");
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-respecify" });
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-replan" });
});
it("rebuilds spec and moves task from done to triage", async () => {
@@ -7186,7 +7186,7 @@ describe("POST /tasks/:id/spec/rebuild", () => {
it("allows rebuild for task already in triage", async () => {
const triageTask = { ...FAKE_TASK_DETAIL, column: "triage" as const };
const updatedTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "needs-respecify" as const };
const updatedTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "needs-replan" as const };
const tempRoot = mkdtempSync(join(tmpdir(), "kb-spec-rebuild-triage-"));
const taskDir = join(tempRoot, ".fusion", "tasks", "FN-001");
mkdirSync(taskDir, { recursive: true });
@@ -7208,7 +7208,7 @@ describe("POST /tasks/:id/spec/rebuild", () => {
);
expect(store.moveTask).not.toHaveBeenCalled();
expect(existsSync(join(taskDir, "PROMPT.md"))).toBe(false);
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-respecify" });
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: "needs-replan" });
} finally {
rmSync(tempRoot, { recursive: true, force: true });
}
@@ -7288,7 +7288,7 @@ describe("POST /tasks/:id/approve-plan", () => {
});
it("returns 400 when task does not have awaiting-approval status", async () => {
const triageTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "specifying" as const };
const triageTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "planning" as const };
(store.getTask as ReturnType<typeof vi.fn>).mockResolvedValue(triageTask);
const res = await REQUEST(buildApp(), "POST", "/api/tasks/KB-001/approve-plan");
@@ -7364,7 +7364,7 @@ describe("POST /tasks/:id/reject-plan", () => {
});
it("returns 400 when task does not have awaiting-approval status", async () => {
const triageTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "specifying" as const };
const triageTask = { ...FAKE_TASK_DETAIL, column: "triage" as const, status: "planning" as const };
(store.getTask as ReturnType<typeof vi.fn>).mockResolvedValue(triageTask);
const res = await REQUEST(buildApp(), "POST", "/api/tasks/KB-001/reject-plan");