diff --git a/packages/dashboard/src/__tests__/routes-planning.test.ts b/packages/dashboard/src/__tests__/routes-planning.test.ts index 471bce4b8..3dcb60288 100644 --- a/packages/dashboard/src/__tests__/routes-planning.test.ts +++ b/packages/dashboard/src/__tests__/routes-planning.test.ts @@ -217,9 +217,8 @@ function createMockStore(overrides: Partial = {}): TaskStore { updatePrInfo: vi.fn().mockResolvedValue(undefined), updateIssueInfo: vi.fn().mockResolvedValue(undefined), getRootDir: vi.fn().mockReturnValue("/fake/root"), - ensureBranchGroupForSource: vi.fn((sourceType: "planning" | "mission", sourceId: string, init: { branchName: string; autoMerge?: boolean }) => { - const key = `${sourceType}:${sourceId}`; - const existing = branchGroups.get(key); + ensureBranchGroupForSource: vi.fn(function (this: TaskStore, sourceType: "planning" | "mission", sourceId: string, init: { branchName: string; autoMerge?: boolean }) { + const existing = this.getBranchGroupBySource(sourceType, sourceId); if (existing) { return existing; } @@ -234,7 +233,7 @@ function createMockStore(overrides: Partial = {}): TaskStore { createdAt: Date.now(), updatedAt: Date.now(), }; - branchGroups.set(key, created); + branchGroups.set(`${sourceType}:${sourceId}`, created); return created; }), getBranchGroupBySource: vi.fn((sourceType: "planning" | "mission", sourceId: string) => @@ -2126,6 +2125,60 @@ describe("Planning Mode Routes", () => { ); }); + it("ensures shared branch groups when creating subtask breakdown tasks", async () => { + (store.createTask as ReturnType) + .mockResolvedValueOnce({ + id: "FN-281", + description: "First", + column: "triage", + dependencies: [], + createdAt: "2026-01-01T00:00:00.000Z", + updatedAt: "2026-01-01T00:00:00.000Z", + }) + .mockResolvedValueOnce({ + id: "FN-282", + description: "Second", + column: "triage", + dependencies: [], + createdAt: "2026-01-01T00:00:00.000Z", + updatedAt: "2026-01-01T00:00:00.000Z", + }); + + const subtaskRes = await REQUEST( + buildApp(), + "POST", + "/api/subtasks/start-streaming", + JSON.stringify({ description: "Break down auth scope" }), + { "Content-Type": "application/json" }, + ); + expect(subtaskRes.status).toBe(201); + + const sessionId = subtaskRes.body.sessionId as string; + const createRes = await REQUEST( + buildApp(), + "POST", + "/api/subtasks/create-tasks", + JSON.stringify({ + sessionId, + branchSelection: { mode: "custom-new", branchName: "feature/auth-breakdown", baseBranch: "main" }, + branchAssignment: { mode: "shared" }, + subtasks: [ + { tempId: "temp-1", title: "Auth backend", description: "Implement backend" }, + { tempId: "temp-2", title: "Auth UI", description: "Implement UI", dependsOn: ["temp-1"] }, + ], + }), + { "Content-Type": "application/json" }, + ); + + expect(createRes.status).toBe(201); + expect(store.ensureBranchGroupForSource).toHaveBeenCalledWith( + "planning", + sessionId, + expect.objectContaining({ branchName: "feature/auth-breakdown", autoMerge: false }), + ); + expect(store.getBranchGroupBySource).toHaveBeenCalledWith("planning", sessionId); + }); + it("prefers session autoMerge override when creating shared planning subtasks", async () => { (store.createTask as ReturnType) .mockResolvedValueOnce({ diff --git a/packages/dashboard/src/routes/register-planning-subtask-routes.ts b/packages/dashboard/src/routes/register-planning-subtask-routes.ts index bf98ea14e..3cd1f0c6e 100644 --- a/packages/dashboard/src/routes/register-planning-subtask-routes.ts +++ b/packages/dashboard/src/routes/register-planning-subtask-routes.ts @@ -224,8 +224,8 @@ export function registerPlanningSubtaskRoutes(ctx: ApiRoutesContext, deps: Plann ? settings.defaultBranch : "main"; const settingsAutoMerge = typeof settings.autoMerge === "boolean" ? settings.autoMerge : false; - const ensureBranchGroupForSource = (scopedStore as { ensureBranchGroupForSource?: TaskStore["ensureBranchGroupForSource"] }).ensureBranchGroupForSource; - ensureBranchGroupForSource?.("planning", sessionId, { + const branchGroupStore = scopedStore as { ensureBranchGroupForSource?: TaskStore["ensureBranchGroupForSource"] }; + branchGroupStore.ensureBranchGroupForSource?.("planning", sessionId, { branchName: resolvedBranch ?? resolvedBaseBranch ?? settingsDefaultBranch, autoMerge: session.autoMerge ?? settingsAutoMerge, }); @@ -1279,8 +1279,8 @@ export function registerPlanningSubtaskRoutes(ctx: ApiRoutesContext, deps: Plann ? settings.defaultBranch : "main"; const settingsAutoMerge = typeof settings.autoMerge === "boolean" ? settings.autoMerge : false; - const ensureBranchGroupForSource = (scopedStore as { ensureBranchGroupForSource?: TaskStore["ensureBranchGroupForSource"] }).ensureBranchGroupForSource; - ensureBranchGroupForSource?.("planning", planningSessionId, { + const branchGroupStore = scopedStore as { ensureBranchGroupForSource?: TaskStore["ensureBranchGroupForSource"] }; + branchGroupStore.ensureBranchGroupForSource?.("planning", planningSessionId, { branchName: resolvedBranch ?? resolvedBaseBranch ?? settingsDefaultBranch, autoMerge: session.autoMerge ?? settingsAutoMerge, });