Q151: deleting a task first shows what goes with it, then waits for the result

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-09 01:21:31 -04:00
1 parent 809c67a54a
commit 18f1e1fcc3
13 files changed
+171 -5

No files matched your search

+41
View File
@@ -251,6 +251,47 @@ test("the pinned CEO conversation is the Messages front door", async () => {
await page.locator(".convo-item").first().click();
await page.getByRole("button", { name: /^Members \(/ }).click();
await expect(page.getByRole("menu")).toContainText("Replies to every message");
await page.keyboard.press("Escape");
// Deleting a task first says what it takes with it, then waits for the result.
await page.evaluate(async () => {
const api = (window as unknown as { agentCompany: {
loadSnapshot: () => Promise<{ ok: true; data: { activeOrgId: string; tasks: Array<{ id: string; title: string }> } }>;
createTask: (input: unknown) => Promise<{ ok: boolean }>;
updateTask: (input: unknown) => Promise<{ ok: boolean }>;
} }).agentCompany;
const before = (await api.loadSnapshot()).data;
const parent = before.tasks.find((task) => task.title === "Operationalize goal: Ship the FlowPilot MVP.")!;
await api.createTask({
orgId: before.activeOrgId, title: "Child step", description: "d", status: "backlog", assigneeAgentId: null, risk: "low",
projectId: null, goalId: null, workspaceId: null, priority: "normal", requiresFilesystem: false,
});
const child = (await api.loadSnapshot()).data.tasks.find((task) => task.title === "Child step")!;
await api.updateTask({
orgId: before.activeOrgId, taskId: child.id, title: "Child step", description: "d", assigneeAgentId: null, risk: "low", priority: "normal",
parentTaskId: parent.id, projectId: null, goalId: null, workspaceId: null, requiresFilesystem: false, writingMode: "conversational",
});
});
await page.getByRole("button", { name: "Work" }).click();
await page.getByRole("button", { name: /Operationalize goal: Ship the FlowPilot MVP\./ }).first().click();
const parentDialog = page.getByRole("dialog", { name: "Operationalize goal: Ship the FlowPilot MVP." });
await parentDialog.getByRole("button", { name: "Delete", exact: true }).click();
await expect(parentDialog.getByRole("alertdialog", { name: "Delete this task?" })).toContainText("1 subtasks stay and move to the top level.");
// Switching to another task drops a pending confirmation, so it can never delete the wrong task.
await parentDialog.getByRole("button", { name: "Subtasks" }).click();
await parentDialog.getByRole("button", { name: "Open task Child step" }).click();
const childDialog = page.getByRole("dialog", { name: "Child step" });
await expect(childDialog).toBeVisible();
await expect(childDialog.getByRole("alertdialog")).toHaveCount(0);
await childDialog.getByRole("button", { name: "Close" }).click();
await page.getByRole("button", { name: /Operationalize goal: Ship the FlowPilot MVP\./ }).first().click();
const doomed = page.getByRole("dialog", { name: "Operationalize goal: Ship the FlowPilot MVP." });
await doomed.getByRole("button", { name: "Delete", exact: true }).click();
const confirm = doomed.getByRole("alertdialog", { name: "Delete this task?" });
await expect(confirm).toContainText("sessions and their action records are deleted.");
await confirm.getByRole("button", { name: "Delete task" }).click();
await expect(doomed).toHaveCount(0);
await expect(page.getByRole("button", { name: /Operationalize goal: Ship the FlowPilot MVP\./ })).toHaveCount(0);
} finally {
await app.close();
}
+37
View File
@@ -0,0 +1,37 @@
import { randomUUID } from "node:crypto";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vitest";
import { DatabaseConnection } from "../../src/main/database/connection";
import { OrganizationService } from "../../src/main/services/organization-service";
import { DocumentService } from "../../src/main/services/document-service";
import { RISK_LEVEL, TASK_PRIORITY, TASK_STATUS } from "../../src/shared/domain";
describe("deleting a task", () => {
test("says beforehand what goes with it, what stays, and what stops it", () => {
const connection = new DatabaseConnection(join(tmpdir(), `ac-delete-impact-${randomUUID()}.db`));
const organization = new OrganizationService(connection.db);
const ids: Record<string, string> = {};
const task = (title: string) => organization.createTask({
orgId: ids.org, title, description: "d", status: TASK_STATUS.BACKLOG, assigneeAgentId: null, risk: RISK_LEVEL.LOW,
projectId: null, goalId: null, workspaceId: null, priority: TASK_PRIORITY.NORMAL, requiresFilesystem: false,
});
connection.transaction(() => {
ids.org = organization.createOrganization({ organizationName: "Acme", employeeName: "Yang", mission: "m", goal: "g" });
ids.agent = organization.createAgent({ orgId: ids.org, name: "Ada", role: "Engineer", department: "Engineering", modelProviderId: null, modelId: null, budgetCents: 1000, managerAgentId: null });
ids.task = task("Parent");
ids.sub = task("Child");
connection.db.prepare("update tasks set parent_task_id = ? where id = ?").run(ids.task, ids.sub);
new DocumentService(connection.db).create({ orgId: ids.org, title: "Spec", docType: "spec", content: "c", authorAgentId: null, reviewerAgentId: null, projectId: null, goalId: null, taskId: ids.task });
for (const state of ["completed", "failed"]) {
connection.db.prepare("insert into task_runs(id, org_id, task_id, agent_id, trigger, state, created_at) values (?, ?, ?, ?, 'manual', ?, ?)")
.run(`run_${state}`, ids.org, ids.task, ids.agent, state, new Date().toISOString());
}
});
expect(organization.taskDeleteImpact(ids.org, ids.task)).toEqual({ runs: 2, running: 0, subtasks: 1, documents: 1, openReviews: 0, pendingApprovals: 0 });
connection.db.prepare("update task_runs set state = 'running' where id = 'run_failed'").run();
expect(organization.taskDeleteImpact(ids.org, ids.task).running).toBe(1);
expect(() => organization.taskDeleteImpact(ids.org, "tsk_missing")).toThrow("Task was not found.");
connection.close();
});
});