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

@@ -46,6 +46,7 @@ export const organizationContracts = {
}) satisfies z.ZodType<C.UpdateTaskInput>,
deleteTask: z.strictObject({ orgId: id, taskId: id }) satisfies z.ZodType<C.DeleteTaskInput>,
setTaskBlocked: z.strictObject({ orgId: id, taskId: id, reason: z.string().nullable() }) satisfies z.ZodType<C.SetTaskBlockedInput>,
taskDeleteImpact: z.strictObject({ orgId: id, taskId: id }),
updateTaskStatus: z.strictObject({ orgId: id, taskId: id, status: z.enum(TASK_STATUS) }) satisfies z.ZodType<C.UpdateTaskStatusInput>,
runTask: z.strictObject({ orgId: id, taskId: id }) satisfies z.ZodType<C.RunTaskInput>,
createGoal: z.strictObject({ orgId: id, title: z.string(), description: z.string(), ownerAgentId: nullableId }) satisfies z.ZodType<C.CreateGoalInput>,
+1
View File
@@ -165,6 +165,7 @@ export function registerIpcHandlers(deps: IpcDeps): void {
handle("createTask", (input) => mutate(() => organization.createTask(input)));
handle("updateTask", (input) => mutate(() => organization.updateTask(input)));
handle("setTaskBlocked", (input) => mutate(() => organization.setTaskBlocked(input)));
handle("taskDeleteImpact", (input) => ok(organization.taskDeleteImpact(input.orgId, input.taskId)));
handle("deleteTask", (input) => mutate(() => organization.deleteTask(input)));
handle("updateTaskStatus", (input) => mutate(() => organization.updateTaskStatus(input.orgId, input.taskId, input.status)));
handle("runTask", async (input) => {
@@ -1,3 +1,4 @@
import type { TaskDeleteImpact } from "../../shared/domain";
import type { DatabaseSync } from "node:sqlite";
import { APPROVAL_STATE, RISK_LEVEL, TASK_STATUS, WRITING_MODE, type RiskLevel } from "../../shared/domain";
import { createId } from "../../shared/ids";
@@ -472,6 +473,10 @@ export class OrganizationService {
this.tasks.update(input);
}
taskDeleteImpact(orgId: string, taskId: string): TaskDeleteImpact {
return this.tasks.deleteImpact(orgId, taskId);
}
setTaskBlocked(input: SetTaskBlockedInput): void {
this.tasks.setBlocked(input);
}
+15
View File
@@ -1,3 +1,4 @@
import type { TaskDeleteImpact } from "../../shared/domain";
import type { DatabaseSync } from "node:sqlite";
import { RISK_LEVEL, TASK_STATUS, type TaskPriority } from "../../shared/domain";
import type { CreateTaskInput, DeleteTaskInput, SetTaskBlockedInput, UpdateTaskInput } from "../../shared/commands";
@@ -70,6 +71,20 @@ export class TaskMutations {
});
}
/** What deleting the task would remove, keep and close, counted from the database. */
deleteImpact(orgId: string, taskId: string): TaskDeleteImpact {
this.requireTask(orgId, taskId);
const count = (sql: string) => (this.db.prepare(sql).get(taskId, orgId) as { n: number }).n;
return {
runs: count("select count(*) as n from task_runs where task_id = ? and org_id = ?"),
running: count("select count(*) as n from task_runs where task_id = ? and org_id = ? and state = 'running'"),
subtasks: count("select count(*) as n from tasks where parent_task_id = ? and org_id = ?"),
documents: count("select count(*) as n from documents where task_id = ? and org_id = ?"),
openReviews: count("select count(*) as n from tasks where review_of_task_id = ? and org_id = ? and status <> 'done'"),
pendingApprovals: count("select count(*) as n from approvals where source_entity_type = 'task' and source_entity_id = ? and org_id = ? and state = 'pending'"),
};
}
/** Blocks the task with a reason (no session starts, queued ones are superseded) or unblocks it with null. */
setBlocked(input: SetTaskBlockedInput): void {
const reason = input.reason === null ? null : input.reason.trim();