fix(FN-1250): route task-creation endpoints through scoped project stores
- Resolve projectId-scoped TaskStore instances for GitHub issue/PR import and batch import mutations - Use scoped stores for planning and subtask breakdown create flows, including rootDir lookups, dependency updates, and parent cleanup - Add regression tests to verify projectId requests mutate only the scoped store across import, planning, and subtask routes
This commit is contained in:
@@ -16,7 +16,10 @@ import type { TaskDetail } from "@fusion/core";
|
||||
import type { AuthStorageLike, ModelRegistryLike } from "./routes.js";
|
||||
import { __resetBatchImportRateLimiter, __setCreateKbAgentForRefine } from "./routes.js";
|
||||
import { __resetPlanningState, __setCreateKbAgent, planningStreamManager } from "./planning.js";
|
||||
import * as planningModule from "./planning.js";
|
||||
import { __resetSubtaskBreakdownState, subtaskStreamManager } from "./subtask-breakdown.js";
|
||||
import * as subtaskBreakdownModule from "./subtask-breakdown.js";
|
||||
import * as projectStoreResolver from "./project-store-resolver.js";
|
||||
import * as terminalServiceModule from "./terminal-service.js";
|
||||
import { get as performGet, request as performRequest } from "./test-request.js";
|
||||
|
||||
@@ -4233,6 +4236,278 @@ describe("POST /github/issues/batch-import", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("projectId store scoping regressions", () => {
|
||||
const projectId = "proj-scoped";
|
||||
let defaultStore: TaskStore;
|
||||
let scopedStore: TaskStore;
|
||||
|
||||
beforeEach(() => {
|
||||
__resetBatchImportRateLimiter();
|
||||
__resetPlanningState();
|
||||
__resetSubtaskBreakdownState();
|
||||
mockIsGhAuthenticated.mockReturnValue(true);
|
||||
|
||||
defaultStore = createMockStore({
|
||||
listTasks: vi.fn().mockResolvedValue([]),
|
||||
createTask: vi.fn(),
|
||||
updateTask: vi.fn(),
|
||||
logEntry: vi.fn().mockResolvedValue(undefined),
|
||||
getTask: vi.fn(),
|
||||
deleteTask: vi.fn(),
|
||||
getRootDir: vi.fn().mockReturnValue("/fake/default"),
|
||||
});
|
||||
|
||||
scopedStore = createMockStore({
|
||||
listTasks: vi.fn().mockResolvedValue([]),
|
||||
createTask: vi.fn(),
|
||||
updateTask: vi.fn().mockImplementation(async (id: string, patch: Record<string, unknown>) => ({
|
||||
...FAKE_TASK_DETAIL,
|
||||
id,
|
||||
column: "triage",
|
||||
...patch,
|
||||
})),
|
||||
logEntry: vi.fn().mockResolvedValue(undefined),
|
||||
getTask: vi.fn().mockResolvedValue({
|
||||
...FAKE_TASK_DETAIL,
|
||||
id: "FN-PARENT",
|
||||
column: "triage",
|
||||
}),
|
||||
deleteTask: vi.fn().mockResolvedValue(undefined),
|
||||
getRootDir: vi.fn().mockReturnValue("/fake/scoped"),
|
||||
});
|
||||
|
||||
vi.spyOn(projectStoreResolver, "getOrCreateProjectStore").mockResolvedValue(scopedStore);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
__setCreateKbAgent(undefined as any);
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
function buildApp(options?: Parameters<typeof createApiRoutes>[1]) {
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use("/api", createApiRoutes(defaultStore, options));
|
||||
return app;
|
||||
}
|
||||
|
||||
it("routes github issue import mutations to scoped store when projectId is provided", async () => {
|
||||
vi.spyOn(GitHubClient.prototype, "getIssue").mockResolvedValue({
|
||||
number: 1,
|
||||
title: "Scoped issue",
|
||||
body: "Body",
|
||||
html_url: "https://github.com/owner/repo/issues/1",
|
||||
state: "open",
|
||||
});
|
||||
(scopedStore.createTask as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
...FAKE_TASK_DETAIL,
|
||||
id: "FN-SCOPE-1",
|
||||
column: "triage",
|
||||
});
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/github/issues/import",
|
||||
JSON.stringify({ owner: "owner", repo: "repo", issueNumber: 1, projectId }),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
expect(projectStoreResolver.getOrCreateProjectStore).toHaveBeenCalledWith(projectId);
|
||||
expect(scopedStore.createTask).toHaveBeenCalledTimes(1);
|
||||
expect(defaultStore.createTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes github batch-import mutations to scoped store when projectId is provided", async () => {
|
||||
vi.spyOn(GitHubClient.prototype, "fetchThrottled")
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: {
|
||||
number: 1,
|
||||
title: "One",
|
||||
body: "Body one",
|
||||
html_url: "https://github.com/owner/repo/issues/1",
|
||||
},
|
||||
} as Awaited<ReturnType<GitHubClient["fetchThrottled"]>>)
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: {
|
||||
number: 2,
|
||||
title: "Two",
|
||||
body: "Body two",
|
||||
html_url: "https://github.com/owner/repo/issues/2",
|
||||
},
|
||||
} as Awaited<ReturnType<GitHubClient["fetchThrottled"]>>);
|
||||
|
||||
(scopedStore.createTask as ReturnType<typeof vi.fn>)
|
||||
.mockResolvedValueOnce({ ...FAKE_TASK_DETAIL, id: "FN-SCOPE-2", column: "triage" })
|
||||
.mockResolvedValueOnce({ ...FAKE_TASK_DETAIL, id: "FN-SCOPE-3", column: "triage" });
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/github/issues/batch-import",
|
||||
JSON.stringify({ owner: "owner", repo: "repo", issueNumbers: [1, 2], delayMs: 1, projectId }),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(scopedStore.createTask).toHaveBeenCalledTimes(2);
|
||||
expect(defaultStore.createTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes github pull import mutations to scoped store when projectId is provided", async () => {
|
||||
vi.spyOn(GitHubClient.prototype, "getPullRequest").mockResolvedValue({
|
||||
number: 9,
|
||||
title: "Scoped pull",
|
||||
body: "PR body",
|
||||
html_url: "https://github.com/owner/repo/pull/9",
|
||||
headBranch: "feature/branch",
|
||||
baseBranch: "main",
|
||||
state: "open",
|
||||
});
|
||||
|
||||
(scopedStore.createTask as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
...FAKE_TASK_DETAIL,
|
||||
id: "FN-SCOPE-4",
|
||||
column: "triage",
|
||||
});
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/github/pulls/import",
|
||||
JSON.stringify({ owner: "owner", repo: "repo", prNumber: 9, projectId }),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
expect(scopedStore.createTask).toHaveBeenCalledTimes(1);
|
||||
expect(defaultStore.createTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes planning create-task mutations to scoped store when projectId is provided", async () => {
|
||||
vi.spyOn(planningModule, "getSession").mockReturnValue({
|
||||
id: "plan-session-1",
|
||||
initialPlan: "Scoped initial plan",
|
||||
history: [],
|
||||
thinkingOutput: "",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
} as any);
|
||||
vi.spyOn(planningModule, "getSummary").mockReturnValue({
|
||||
title: "Scoped planned task",
|
||||
description: "Create task in scoped project",
|
||||
suggestedSize: "M",
|
||||
suggestedDependencies: [],
|
||||
keyDeliverables: [],
|
||||
});
|
||||
vi.spyOn(planningModule, "cleanupSession").mockImplementation(() => {});
|
||||
|
||||
(scopedStore.createTask as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
...FAKE_TASK_DETAIL,
|
||||
id: "FN-SCOPE-5",
|
||||
column: "triage",
|
||||
});
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/planning/create-task",
|
||||
JSON.stringify({ sessionId: "plan-session-1", projectId }),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
expect(scopedStore.createTask).toHaveBeenCalledTimes(1);
|
||||
expect(defaultStore.createTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes planning create-tasks mutations to scoped store when projectId is provided", async () => {
|
||||
vi.spyOn(planningModule, "getSession").mockReturnValue({
|
||||
id: "plan-session-2",
|
||||
initialPlan: "Scoped multi task plan",
|
||||
history: [],
|
||||
summary: {
|
||||
title: "Plan",
|
||||
description: "Plan description",
|
||||
suggestedSize: "M",
|
||||
suggestedDependencies: [],
|
||||
keyDeliverables: ["Deliverable 1", "Deliverable 2"],
|
||||
},
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
} as any);
|
||||
vi.spyOn(planningModule, "formatInterviewQA").mockReturnValue("Q: Scope\nA: Medium");
|
||||
vi.spyOn(planningModule, "cleanupSession").mockImplementation(() => {});
|
||||
|
||||
(scopedStore.createTask as ReturnType<typeof vi.fn>)
|
||||
.mockResolvedValueOnce({ ...FAKE_TASK_DETAIL, id: "FN-SCOPE-6", column: "triage" })
|
||||
.mockResolvedValueOnce({ ...FAKE_TASK_DETAIL, id: "FN-SCOPE-7", column: "triage" });
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/planning/create-tasks",
|
||||
JSON.stringify({
|
||||
planningSessionId: "plan-session-2",
|
||||
projectId,
|
||||
subtasks: [
|
||||
{ id: "sub-1", title: "First scoped task", description: "First", suggestedSize: "S", dependsOn: [] },
|
||||
{ id: "sub-2", title: "Second scoped task", description: "Second", suggestedSize: "M", dependsOn: ["sub-1"] },
|
||||
],
|
||||
}),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
expect(scopedStore.createTask).toHaveBeenCalledTimes(2);
|
||||
expect(defaultStore.createTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes subtask create-tasks mutations to scoped store when projectId is provided", async () => {
|
||||
vi.spyOn(subtaskBreakdownModule, "getSubtaskSession").mockReturnValue({
|
||||
sessionId: "subtask-session-1",
|
||||
initialDescription: "Break down scoped work",
|
||||
subtasks: [],
|
||||
status: "complete",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
thinkingOutput: "",
|
||||
} as any);
|
||||
vi.spyOn(subtaskBreakdownModule, "cleanupSubtaskSession").mockImplementation(() => {});
|
||||
|
||||
(scopedStore.createTask as ReturnType<typeof vi.fn>)
|
||||
.mockResolvedValueOnce({ ...FAKE_TASK_DETAIL, id: "FN-SCOPE-8", column: "triage" })
|
||||
.mockResolvedValueOnce({ ...FAKE_TASK_DETAIL, id: "FN-SCOPE-9", column: "triage" });
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/subtasks/create-tasks",
|
||||
JSON.stringify({
|
||||
sessionId: "subtask-session-1",
|
||||
projectId,
|
||||
parentTaskId: "FN-PARENT",
|
||||
subtasks: [
|
||||
{ tempId: "temp-1", title: "Scoped subtask one", description: "One", size: "S", dependsOn: [] },
|
||||
{ tempId: "temp-2", title: "Scoped subtask two", description: "Two", size: "M", dependsOn: ["temp-1"] },
|
||||
],
|
||||
}),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
expect(scopedStore.getTask).toHaveBeenCalledWith("FN-PARENT");
|
||||
expect(defaultStore.getTask).not.toHaveBeenCalled();
|
||||
expect(scopedStore.createTask).toHaveBeenCalledTimes(2);
|
||||
expect(defaultStore.createTask).not.toHaveBeenCalled();
|
||||
expect(scopedStore.deleteTask).toHaveBeenCalledWith("FN-PARENT");
|
||||
expect(defaultStore.deleteTask).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
// --- Spec Revision route tests ---
|
||||
|
||||
describe("POST /tasks/:id/spec/revise", () => {
|
||||
|
||||
@@ -3628,6 +3628,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
}
|
||||
|
||||
const client = new GitHubClient();
|
||||
const scopedStore = await getScopedStore(req);
|
||||
|
||||
let issue: {
|
||||
number: number;
|
||||
@@ -3665,7 +3666,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
}
|
||||
|
||||
// Check if already imported
|
||||
const existingTasks = await store.listTasks();
|
||||
const existingTasks = await scopedStore.listTasks();
|
||||
const sourceUrl = issue.html_url;
|
||||
for (const existingTask of existingTasks) {
|
||||
if (existingTask.description.includes(sourceUrl)) {
|
||||
@@ -3682,7 +3683,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const body = issue.body?.trim() || "(no description)";
|
||||
const description = `${body}\n\nSource: ${sourceUrl}`;
|
||||
|
||||
const task = await store.createTask({
|
||||
const task = await scopedStore.createTask({
|
||||
title: title || undefined,
|
||||
description,
|
||||
column: "triage",
|
||||
@@ -3690,7 +3691,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
});
|
||||
|
||||
// Log the import action
|
||||
await store.logEntry(task.id, "Imported from GitHub", sourceUrl);
|
||||
await scopedStore.logEntry(task.id, "Imported from GitHub", sourceUrl);
|
||||
|
||||
res.status(201).json(task);
|
||||
} catch (err: any) {
|
||||
@@ -3778,9 +3779,10 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const githubClient = new GitHubClient(token);
|
||||
const scopedStore = await getScopedStore(req);
|
||||
|
||||
// Get existing tasks to check for duplicates
|
||||
const existingTasks = await store.listTasks();
|
||||
const existingTasks = await scopedStore.listTasks();
|
||||
|
||||
// Process issues sequentially with throttling
|
||||
const results: Array<{
|
||||
@@ -3845,7 +3847,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const description = `${body}\n\nSource: ${sourceUrl}`;
|
||||
|
||||
try {
|
||||
const task = await store.createTask({
|
||||
const task = await scopedStore.createTask({
|
||||
title: title || undefined,
|
||||
description,
|
||||
column: "triage",
|
||||
@@ -3853,7 +3855,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
});
|
||||
|
||||
// Log the import action
|
||||
await store.logEntry(task.id, "Imported from GitHub", sourceUrl);
|
||||
await scopedStore.logEntry(task.id, "Imported from GitHub", sourceUrl);
|
||||
|
||||
results.push({
|
||||
issueNumber,
|
||||
@@ -3964,6 +3966,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
}
|
||||
|
||||
const client = new GitHubClient();
|
||||
const scopedStore = await getScopedStore(req);
|
||||
|
||||
let pr: {
|
||||
number: number;
|
||||
@@ -4001,7 +4004,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
}
|
||||
|
||||
// Check if already imported
|
||||
const existingTasks = await store.listTasks();
|
||||
const existingTasks = await scopedStore.listTasks();
|
||||
const sourceUrl = pr.html_url;
|
||||
for (const existingTask of existingTasks) {
|
||||
if (existingTask.description.includes(sourceUrl)) {
|
||||
@@ -4018,7 +4021,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
const body = pr.body?.trim() || "(no description)";
|
||||
const description = `Review and address any issues in this pull request.\n\nPR: ${sourceUrl}\nBranch: ${pr.headBranch} → ${pr.baseBranch}\n\n${body}`;
|
||||
|
||||
const task = await store.createTask({
|
||||
const task = await scopedStore.createTask({
|
||||
title: title || undefined,
|
||||
description,
|
||||
column: "triage",
|
||||
@@ -4026,7 +4029,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
});
|
||||
|
||||
// Log the import action
|
||||
await store.logEntry(task.id, "Imported PR from GitHub", sourceUrl);
|
||||
await scopedStore.logEntry(task.id, "Imported PR from GitHub", sourceUrl);
|
||||
|
||||
res.status(201).json(task);
|
||||
} catch (err: any) {
|
||||
@@ -5403,8 +5406,9 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
return;
|
||||
}
|
||||
|
||||
const scopedStore = await getScopedStore(req);
|
||||
const { createSubtaskSession } = await import("./subtask-breakdown.js");
|
||||
const session = await createSubtaskSession(description, store, store.getRootDir());
|
||||
const session = await createSubtaskSession(description, scopedStore, scopedStore.getRootDir());
|
||||
res.status(201).json({ sessionId: session.sessionId });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ error: err.message || "Failed to start subtask breakdown" });
|
||||
@@ -5535,6 +5539,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
return;
|
||||
}
|
||||
|
||||
const scopedStore = await getScopedStore(req);
|
||||
const { getSubtaskSession, cleanupSubtaskSession } = await import("./subtask-breakdown.js");
|
||||
const session = getSubtaskSession(sessionId);
|
||||
if (!session) {
|
||||
@@ -5546,7 +5551,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
let parentTask: Awaited<ReturnType<typeof store.getTask>> | undefined;
|
||||
if (typeof parentTaskId === "string" && parentTaskId.trim()) {
|
||||
try {
|
||||
parentTask = await store.getTask(parentTaskId);
|
||||
parentTask = await scopedStore.getTask(parentTaskId);
|
||||
} catch {
|
||||
// Parent task not found or error - proceed without inheritance
|
||||
parentTask = undefined;
|
||||
@@ -5562,7 +5567,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
return;
|
||||
}
|
||||
|
||||
const task = await store.createTask({
|
||||
const task = await scopedStore.createTask({
|
||||
title: item.title.trim(),
|
||||
description: typeof item.description === "string" ? item.description.trim() : item.title.trim(),
|
||||
column: "triage",
|
||||
@@ -5578,7 +5583,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
createdTasks.push(task);
|
||||
|
||||
if (item.size === "S" || item.size === "M" || item.size === "L") {
|
||||
await store.updateTask(task.id, { size: item.size });
|
||||
await scopedStore.updateTask(task.id, { size: item.size });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5590,17 +5595,17 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
: [];
|
||||
|
||||
if (resolvedDependencies.length > 0) {
|
||||
const updated = await store.updateTask(created.id, { dependencies: resolvedDependencies });
|
||||
const updated = await scopedStore.updateTask(created.id, { dependencies: resolvedDependencies });
|
||||
createdTasks[index] = updated;
|
||||
}
|
||||
|
||||
await store.logEntry(created.id, "Created via subtask breakdown", `Source: ${session.initialDescription.slice(0, 200)}`);
|
||||
await scopedStore.logEntry(created.id, "Created via subtask breakdown", `Source: ${session.initialDescription.slice(0, 200)}`);
|
||||
}
|
||||
|
||||
let parentTaskClosed = false;
|
||||
if (typeof parentTaskId === "string" && parentTaskId.trim()) {
|
||||
try {
|
||||
await store.deleteTask(parentTaskId);
|
||||
await scopedStore.deleteTask(parentTaskId);
|
||||
parentTaskClosed = true;
|
||||
} catch {
|
||||
parentTaskClosed = false;
|
||||
@@ -5654,11 +5659,12 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
return;
|
||||
}
|
||||
|
||||
const scopedStore = await getScopedStore(req);
|
||||
const ip = req.ip || req.socket.remoteAddress || "unknown";
|
||||
const rootDir = store.getRootDir();
|
||||
const rootDir = scopedStore.getRootDir();
|
||||
|
||||
const { createSession, RateLimitError } = await import("./planning.js");
|
||||
const result = await createSession(ip, initialPlan, store, rootDir);
|
||||
const result = await createSession(ip, initialPlan, scopedStore, rootDir);
|
||||
res.status(201).json(result);
|
||||
} catch (err: any) {
|
||||
if (err.name === "RateLimitError") {
|
||||
@@ -5702,8 +5708,9 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
return;
|
||||
}
|
||||
|
||||
const scopedStore = await getScopedStore(req);
|
||||
const ip = req.ip || req.socket.remoteAddress || "unknown";
|
||||
const rootDir = store.getRootDir();
|
||||
const rootDir = scopedStore.getRootDir();
|
||||
|
||||
const { createSessionWithAgent, RateLimitError } = await import("./planning.js");
|
||||
const sessionId = await createSessionWithAgent(
|
||||
@@ -5798,6 +5805,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
return;
|
||||
}
|
||||
|
||||
const scopedStore = await getScopedStore(req);
|
||||
const { getSession, getSummary, cleanupSession } = await import("./planning.js");
|
||||
|
||||
const session = getSession(sessionId);
|
||||
@@ -5885,7 +5893,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
}
|
||||
|
||||
// Create the task
|
||||
const task = await store.createTask({
|
||||
const task = await scopedStore.createTask({
|
||||
title: summary.title,
|
||||
description: summary.description,
|
||||
column: "triage",
|
||||
@@ -5894,11 +5902,11 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
|
||||
// Update task with suggested size if provided
|
||||
if (summary.suggestedSize) {
|
||||
await store.updateTask(task.id, { size: summary.suggestedSize });
|
||||
await scopedStore.updateTask(task.id, { size: summary.suggestedSize });
|
||||
}
|
||||
|
||||
// Log the planning mode creation
|
||||
await store.logEntry(task.id, "Created via Planning Mode", `Initial plan: ${(initialPlan ?? "").slice(0, 200)}`);
|
||||
await scopedStore.logEntry(task.id, "Created via Planning Mode", `Initial plan: ${(initialPlan ?? "").slice(0, 200)}`);
|
||||
|
||||
// Cleanup the session
|
||||
if (usedPersistedFallback) {
|
||||
@@ -5984,6 +5992,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
return;
|
||||
}
|
||||
|
||||
const scopedStore = await getScopedStore(req);
|
||||
const { getSession, cleanupSession, formatInterviewQA } = await import("./planning.js");
|
||||
|
||||
const session = getSession(planningSessionId);
|
||||
@@ -6015,7 +6024,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
|
||||
// Create tasks
|
||||
for (const item of subtasks) {
|
||||
const task = await store.createTask({
|
||||
const task = await scopedStore.createTask({
|
||||
title: item.title.trim(),
|
||||
description: typeof item.description === "string" ? item.description.trim() : item.title.trim(),
|
||||
column: "triage",
|
||||
@@ -6026,7 +6035,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
createdTasks.push(task);
|
||||
|
||||
if (item.suggestedSize === "S" || item.suggestedSize === "M" || item.suggestedSize === "L") {
|
||||
await store.updateTask(task.id, { size: item.suggestedSize });
|
||||
await scopedStore.updateTask(task.id, { size: item.suggestedSize });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6039,11 +6048,11 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
: [];
|
||||
|
||||
if (resolvedDependencies.length > 0) {
|
||||
const updated = await store.updateTask(created.id, { dependencies: resolvedDependencies });
|
||||
const updated = await scopedStore.updateTask(created.id, { dependencies: resolvedDependencies });
|
||||
createdTasks[index] = updated;
|
||||
}
|
||||
|
||||
await store.logEntry(created.id, "Created via Planning Mode (multi-task)", logDetails);
|
||||
await scopedStore.logEntry(created.id, "Created via Planning Mode (multi-task)", logDetails);
|
||||
}
|
||||
|
||||
// Cleanup the planning session
|
||||
|
||||
Reference in New Issue
Block a user