feat(FN-3410): finalize planning verification and launcher updates
- Rework planning subtask descriptions and update planning logic/tests for clearer execution guidance - Add launcher/update notice handling in cli-alias with package-config coverage and release script updates - Refine agent detail and agents list UI/CSS for compressed mobile header behavior with updated dashboard tests - Add changesets and related docs/readme touch-ups for release tracking - Ref: runfusion/fusion#33
This commit is contained in:
@@ -22,6 +22,7 @@ Use the 💡 button to open planning mode:
|
||||
- AI reasoning (thinking output) is preserved and visible throughout the session — expand the reasoning toggle to review the model's analysis before answering each question or accepting the summary
|
||||
- Produces summary + key deliverables
|
||||
- Create one task or **Break into Tasks** (multi-task generation with dependencies)
|
||||
- Break-into-tasks descriptions are structured with subtask-specific guidance first, then a separate larger-plan context section (plus `## Planning Interview Context` when interview history exists)
|
||||
- Sessions persist when the modal is closed — resume from the sidebar list at any time; reasoning context is restored automatically
|
||||
|
||||
### 3) Todo item → Plan Mode
|
||||
|
||||
@@ -21,6 +21,7 @@ AI-guided interactive planning for creating well-specified tasks from high-level
|
||||
- Suggested dependencies from existing tasks
|
||||
- Key deliverables checklist
|
||||
5. Create the task directly from the summary
|
||||
6. Or use **Break into Tasks** to generate multiple subtasks where each description starts with subtask-specific implementation guidance, followed by a separate larger-plan context section (including planning interview context when available)
|
||||
|
||||
**Features**:
|
||||
- **Rate Limiting**: Maximum 5 planning sessions per hour per IP
|
||||
|
||||
@@ -1974,13 +1974,18 @@ describe("planning module", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("appends planning interview context to subtask descriptions when history exists", async () => {
|
||||
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");
|
||||
|
||||
const result = generateSubtasksFromPlanning(sessionId);
|
||||
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
expect(result.length).toBe(3);
|
||||
expect(result[0]?.description).toContain('Implement "Implementation" as this subtask\'s primary outcome.');
|
||||
expect(result[1]?.description).toContain('Implement "Tests" as this subtask\'s primary outcome.');
|
||||
expect(result[2]?.description).toContain('Implement "Documentation" as this subtask\'s primary outcome.');
|
||||
|
||||
expect(result[0]?.description).toContain("## Larger Plan Context");
|
||||
expect(result[0]?.description).toContain("## Planning Interview Context");
|
||||
expect(result[0]?.description).toContain("**Q: What is the scope of this plan?**");
|
||||
expect(result[0]?.description).toContain("A: Medium");
|
||||
@@ -1990,7 +1995,7 @@ describe("planning module", () => {
|
||||
expect(result[0]?.description).toContain("A: Yes");
|
||||
});
|
||||
|
||||
it("keeps subtask descriptions unchanged when history is empty", async () => {
|
||||
it("keeps larger-plan context section when history is empty", async () => {
|
||||
const mockIp = getUniqueIp();
|
||||
const sessionId = await createCompletedSession(mockIp, "Build auth without context");
|
||||
|
||||
@@ -2005,7 +2010,9 @@ describe("planning module", () => {
|
||||
const result = generateSubtasksFromPlanning(sessionId);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
for (const subtask of result) {
|
||||
expect(subtask.description).toBe(session.summary.description);
|
||||
expect(subtask.description).toContain("## Larger Plan Context");
|
||||
expect(subtask.description).toContain(session.summary.description);
|
||||
expect(subtask.description).not.toContain("## Planning Interview Context");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2049,6 +2056,10 @@ describe("planning module", () => {
|
||||
suggestedSize: "S",
|
||||
dependsOn: ["subtask-2"],
|
||||
});
|
||||
expect(result[0]?.description).toContain("Define the implementation approach for the plan");
|
||||
expect(result[1]?.description).toContain("Implement the core code changes described by the plan");
|
||||
expect(result[2]?.description).toContain("Verify the implementation end-to-end");
|
||||
expect(result[0]?.description).toContain("## Larger Plan Context");
|
||||
});
|
||||
|
||||
it("assigns correct sizes based on deliverable position", async () => {
|
||||
|
||||
@@ -2143,6 +2143,23 @@ export function getSummary(sessionId: string): PlanningSummary | undefined {
|
||||
* @param sessionId - The planning session ID
|
||||
* @returns Array of SubtaskItem with titles derived from keyDeliverables, or fallback
|
||||
*/
|
||||
function buildPlanningSubtaskDescription(input: {
|
||||
taskGuidance: string;
|
||||
summaryDescription: string;
|
||||
qaSection: string;
|
||||
}): string {
|
||||
const contextSections = [
|
||||
"## Larger Plan Context",
|
||||
input.summaryDescription,
|
||||
];
|
||||
|
||||
if (input.qaSection) {
|
||||
contextSections.push(input.qaSection);
|
||||
}
|
||||
|
||||
return `${input.taskGuidance}\n\n${contextSections.join("\n\n")}`;
|
||||
}
|
||||
|
||||
export function generateSubtasksFromPlanning(sessionId: string): SubtaskItem[] {
|
||||
const session = sessions.get(sessionId);
|
||||
if (!session) return [];
|
||||
@@ -2150,9 +2167,6 @@ export function generateSubtasksFromPlanning(sessionId: string): SubtaskItem[] {
|
||||
|
||||
const { summary } = session;
|
||||
const qaSection = formatInterviewQA(session.history);
|
||||
const descriptionWithContext = qaSection
|
||||
? `${summary.description}\n\n${qaSection}`
|
||||
: summary.description;
|
||||
|
||||
// If key deliverables exist, create one subtask per deliverable
|
||||
if (summary.keyDeliverables.length > 0) {
|
||||
@@ -2162,7 +2176,11 @@ export function generateSubtasksFromPlanning(sessionId: string): SubtaskItem[] {
|
||||
return {
|
||||
id,
|
||||
title: deliverable,
|
||||
description: descriptionWithContext,
|
||||
description: buildPlanningSubtaskDescription({
|
||||
taskGuidance: `Implement "${deliverable}" as this subtask's primary outcome. Focus only on the concrete changes needed to deliver this item.`,
|
||||
summaryDescription: summary.description,
|
||||
qaSection,
|
||||
}),
|
||||
suggestedSize: index === 0 ? "S" as const : index === summary.keyDeliverables.length - 1 ? "S" as const : "M" as const,
|
||||
dependsOn,
|
||||
};
|
||||
@@ -2174,21 +2192,33 @@ export function generateSubtasksFromPlanning(sessionId: string): SubtaskItem[] {
|
||||
{
|
||||
id: "subtask-1",
|
||||
title: "Define implementation approach",
|
||||
description: descriptionWithContext,
|
||||
description: buildPlanningSubtaskDescription({
|
||||
taskGuidance: "Define the implementation approach for the plan, including architecture and sequencing decisions needed before coding.",
|
||||
summaryDescription: summary.description,
|
||||
qaSection,
|
||||
}),
|
||||
suggestedSize: "S" as const,
|
||||
dependsOn: [],
|
||||
},
|
||||
{
|
||||
id: "subtask-2",
|
||||
title: "Implement core changes",
|
||||
description: descriptionWithContext,
|
||||
description: buildPlanningSubtaskDescription({
|
||||
taskGuidance: "Implement the core code changes described by the plan, using the agreed approach from the prior subtask.",
|
||||
summaryDescription: summary.description,
|
||||
qaSection,
|
||||
}),
|
||||
suggestedSize: "M" as const,
|
||||
dependsOn: ["subtask-1"],
|
||||
},
|
||||
{
|
||||
id: "subtask-3",
|
||||
title: "Verify and polish",
|
||||
description: descriptionWithContext,
|
||||
description: buildPlanningSubtaskDescription({
|
||||
taskGuidance: "Verify the implementation end-to-end, then polish quality items like tests, docs, and edge-case handling.",
|
||||
summaryDescription: summary.description,
|
||||
qaSection,
|
||||
}),
|
||||
suggestedSize: "S" as const,
|
||||
dependsOn: ["subtask-2"],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user