feat(FN-3716): add planning mode priority controls for task routes and subt

Adds per-subtask priority controls to planning mode with three implementation steps (route priority support, UI controls, and design-token polish), gates research tools behind an experimental feature flag with a new core infrastructure module, and introduces a WhatsApp chat plugin with discovery and

Fusion-Task-Id: FN-3716
This commit is contained in:
Fusion
2026-05-07 15:58:52 -07:00
committed by gsxdsm
parent aaaebdcb0d
commit c6d12ba5af
14 changed files with 614 additions and 192 deletions

View File

@@ -2068,6 +2068,7 @@ describe("planning module", () => {
title: "Implementation",
description: expect.any(String),
suggestedSize: "S",
priority: "normal",
dependsOn: [],
});
@@ -2077,6 +2078,7 @@ describe("planning module", () => {
title: "Tests",
description: expect.any(String),
suggestedSize: "M",
priority: "normal",
dependsOn: ["subtask-1"],
});
@@ -2086,10 +2088,26 @@ describe("planning module", () => {
title: "Documentation",
description: expect.any(String),
suggestedSize: "S",
priority: "normal",
dependsOn: ["subtask-2"],
});
});
it("inherits summary priority for generated subtasks", async () => {
const mockIp = getUniqueIp();
const sessionId = await createCompletedSession(mockIp, "Build auth with urgent priority");
const session = getSession(sessionId);
if (!session?.summary) {
throw new Error("Expected summary to exist for completed session");
}
session.summary.priority = "urgent";
const result = generateSubtasksFromPlanning(sessionId);
expect(result.length).toBeGreaterThan(0);
expect(result.every((subtask) => subtask.priority === "urgent")).toBe(true);
});
it("generates deliverable subtasks with distinct lead guidance plus separate plan context", async () => {
const mockIp = getUniqueIp();
const sessionId = await createCompletedSession(mockIp, "Build auth system with context");
@@ -2156,6 +2174,7 @@ describe("planning module", () => {
title: "Define implementation approach",
description: expect.any(String),
suggestedSize: "S",
priority: "normal",
dependsOn: [],
});
expect(result[1]).toEqual({
@@ -2163,6 +2182,7 @@ describe("planning module", () => {
title: "Implement core changes",
description: expect.any(String),
suggestedSize: "M",
priority: "normal",
dependsOn: ["subtask-1"],
});
expect(result[2]).toEqual({
@@ -2170,6 +2190,7 @@ describe("planning module", () => {
title: "Verify and polish",
description: expect.any(String),
suggestedSize: "S",
priority: "normal",
dependsOn: ["subtask-2"],
});
expect(result[0]?.description).toContain("Define the implementation approach for the plan");

View File

@@ -1400,6 +1400,7 @@ describe("Planning Mode Routes", () => {
title: "Edited auth task",
description: "Edited description from summary view",
dependencies: ["FN-500"],
priority: "normal",
}),
);
expect(store.updateTask).toHaveBeenCalledWith("FN-099", { size: "S" });
@@ -1473,6 +1474,130 @@ describe("Planning Mode Routes", () => {
expect(mockAiSessionStore.delete).toHaveBeenCalledWith(sessionId);
});
it("creates task with explicit summary priority", async () => {
(store.createTask as ReturnType<typeof vi.fn>).mockResolvedValue({
id: "FN-100",
description: "Priority task",
column: "triage",
dependencies: [],
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
});
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue({});
(store.logEntry as ReturnType<typeof vi.fn>).mockResolvedValue(undefined);
const startRes = await REQUEST(
buildApp(),
"POST",
"/api/planning/start",
JSON.stringify({ initialPlan: "Build a user auth system" }),
{ "Content-Type": "application/json" }
);
const sessionId = startRes.body.sessionId;
await REQUEST(buildApp(), "POST", "/api/planning/respond", JSON.stringify({ sessionId, responses: { scope: "medium" } }), { "Content-Type": "application/json" });
await REQUEST(buildApp(), "POST", "/api/planning/respond", JSON.stringify({ sessionId, responses: { requirements: "Must have login" } }), { "Content-Type": "application/json" });
await REQUEST(buildApp(), "POST", "/api/planning/respond", JSON.stringify({ sessionId, responses: { confirm: true } }), { "Content-Type": "application/json" });
const res = await REQUEST(
buildApp(),
"POST",
"/api/planning/create-task",
JSON.stringify({
sessionId,
summary: {
title: "Priority auth task",
description: "High-priority planning output",
suggestedSize: "M",
priority: "high",
suggestedDependencies: [],
keyDeliverables: ["Login flow"],
},
}),
{ "Content-Type": "application/json" }
);
expect(res.status).toBe(201);
expect(store.createTask).toHaveBeenCalledWith(
expect.objectContaining({
title: "Priority auth task",
priority: "high",
}),
);
});
it("creates multiple planning tasks with per-subtask priorities and defaults", async () => {
(store.createTask as ReturnType<typeof vi.fn>)
.mockResolvedValueOnce({
id: "FN-201",
description: "First",
column: "triage",
dependencies: [],
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
})
.mockResolvedValueOnce({
id: "FN-202",
description: "Second",
column: "triage",
dependencies: [],
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
});
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue({});
(store.logEntry as ReturnType<typeof vi.fn>).mockResolvedValue(undefined);
const startRes = await REQUEST(
buildApp(),
"POST",
"/api/planning/start",
JSON.stringify({ initialPlan: "Build a user auth system" }),
{ "Content-Type": "application/json" }
);
const planningSessionId = startRes.body.sessionId;
await REQUEST(buildApp(), "POST", "/api/planning/respond", JSON.stringify({ sessionId: planningSessionId, responses: { scope: "medium" } }), { "Content-Type": "application/json" });
await REQUEST(buildApp(), "POST", "/api/planning/respond", JSON.stringify({ sessionId: planningSessionId, responses: { requirements: "Must have login" } }), { "Content-Type": "application/json" });
await REQUEST(buildApp(), "POST", "/api/planning/respond", JSON.stringify({ sessionId: planningSessionId, responses: { confirm: true } }), { "Content-Type": "application/json" });
const res = await REQUEST(
buildApp(),
"POST",
"/api/planning/create-tasks",
JSON.stringify({
planningSessionId,
subtasks: [
{
id: "subtask-1",
title: "Auth backend",
description: "Implement backend",
suggestedSize: "M",
priority: "urgent",
dependsOn: [],
},
{
id: "subtask-2",
title: "Auth UI",
description: "Implement UI",
suggestedSize: "S",
dependsOn: ["subtask-1"],
},
],
}),
{ "Content-Type": "application/json" }
);
expect(res.status).toBe(201);
expect(store.createTask).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ title: "Auth backend", priority: "urgent" }),
);
expect(store.createTask).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ title: "Auth UI", priority: "normal" }),
);
});
it("returns 400 if session is not complete", async () => {
// Create a session but don't complete it
const startRes = await REQUEST(