[taskboard] add completion sync APIs

This commit is contained in:
2026-03-07 12:53:22 -08:00
parent e8e79c7b4c
commit 73da5ae6d2
9 changed files with 249 additions and 1 deletions

View File

@@ -102,6 +102,9 @@ export function normalizeTask(row: DatabaseTaskRow): TaskRecord {
preferred_agent: row.preferred_agent || null,
reasoning_effort: row.reasoning_effort || null,
model_hint: row.model_hint || null,
result_summary: row.result_summary || null,
result_detail: row.result_detail || null,
completed_by: row.completed_by || null,
last_dispatch_at: row.last_dispatch_at || null,
acknowledged_at: row.acknowledged_at || null,
last_error: row.last_error || null,
@@ -252,8 +255,9 @@ export async function createTask(input: Partial<TaskRecord>) {
title, description, assignee, family, target_host, target_channel,
dispatch_method, dispatch_state, template_key, repo_slug, base_branch,
preferred_agent, reasoning_effort, model_hint, priority, status, tags,
result_summary, result_detail, completed_by,
last_dispatch_at, acknowledged_at, last_error
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
input.title?.trim() || "",
input.description || "",
@@ -272,6 +276,9 @@ export async function createTask(input: Partial<TaskRecord>) {
input.priority || "Medium",
input.status || "Backlog",
JSON.stringify(tags),
normalizeNullableString(input.result_summary),
normalizeNullableString(input.result_detail),
normalizeNullableString(input.completed_by),
input.last_dispatch_at || null,
deriveAcknowledgedAt(dispatchState),
normalizeNullableString(input.last_error),
@@ -317,6 +324,7 @@ export async function updateTask(id: number, input: Partial<TaskRecord>) {
SET title = ?, description = ?, assignee = ?, family = ?, target_host = ?, target_channel = ?,
dispatch_method = ?, dispatch_state = ?, template_key = ?, repo_slug = ?, base_branch = ?,
preferred_agent = ?, reasoning_effort = ?, model_hint = ?, priority = ?, status = ?, tags = ?,
result_summary = ?, result_detail = ?, completed_by = ?,
last_dispatch_at = ?, acknowledged_at = ?, last_error = ?, completed_at = ?, updated_at = datetime('now')
WHERE id = ?`,
[
@@ -337,6 +345,9 @@ export async function updateTask(id: number, input: Partial<TaskRecord>) {
input.priority ?? existing.priority,
nextStatus,
JSON.stringify(mergedTags),
hasField("result_summary") ? input.result_summary ?? null : existing.result_summary,
hasField("result_detail") ? input.result_detail ?? null : existing.result_detail,
hasField("completed_by") ? input.completed_by ?? null : existing.completed_by,
hasField("last_dispatch_at") ? input.last_dispatch_at ?? null : existing.last_dispatch_at,
acknowledgedAt,
hasField("last_error") ? input.last_error ?? null : existing.last_error,
@@ -373,3 +384,48 @@ export async function updateTask(id: number, input: Partial<TaskRecord>) {
return updated;
}
export async function applyTaskCallback(id: number, payload: {
status?: TaskStatus;
dispatch_state?: DispatchState;
summary?: string | null;
detail?: string | null;
completed_by?: string | null;
last_error?: string | null;
}) {
const nextStatus = payload.status ?? (payload.dispatch_state === "completed" ? "Done" : undefined);
const updated = await updateTask(id, {
status: nextStatus,
dispatch_state: payload.dispatch_state,
result_summary: payload.summary ?? undefined,
result_detail: payload.detail ?? undefined,
completed_by: payload.completed_by ?? undefined,
last_error: payload.last_error ?? undefined,
});
if (!updated) {
return null;
}
const eventType =
payload.dispatch_state === "failed"
? "dispatch_failed"
: payload.dispatch_state === "completed"
? "dispatch_succeeded"
: payload.dispatch_state === "acknowledged"
? "acknowledged"
: "updated";
await appendTaskEvent({
taskId: updated.id,
assignee: updated.assignee,
family: updated.family,
host: updated.target_host,
eventType,
state: updated.dispatch_state,
summary: payload.summary || `${eventType.replace(/_/g, " ")} callback`,
detail: payload.detail || "",
});
return updated;
}