diff --git a/apps/presentation/dashboard/smoke/assets/todo-lifecycle/after-deferred.png b/apps/presentation/dashboard/smoke/assets/todo-lifecycle/after-deferred.png new file mode 100644 index 0000000000..acc17e72dd Binary files /dev/null and b/apps/presentation/dashboard/smoke/assets/todo-lifecycle/after-deferred.png differ diff --git a/apps/presentation/dashboard/smoke/assets/todo-lifecycle/before-deferred.png b/apps/presentation/dashboard/smoke/assets/todo-lifecycle/before-deferred.png new file mode 100644 index 0000000000..fec4128e03 Binary files /dev/null and b/apps/presentation/dashboard/smoke/assets/todo-lifecycle/before-deferred.png differ diff --git a/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx b/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx index 506cc30ba2..4ced7c7da0 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx @@ -68,7 +68,7 @@ const focusableSelector = [ "[tabindex]:not([tabindex='-1'])", ].join(","); -type TodoOperation = "block" | "complete" | "defer" | "successor_create"; +type TodoOperation = "block" | "resume" | "complete" | "defer" | "successor_create"; type GoalSubagentPreview = { codexHostCapacity?: WorkspaceGoalSubagentConfiguration["codexHostCapacity"]; @@ -422,7 +422,8 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention normalizedParameters: { agent_id: todo.claimedBy ?? todoAgentId, goal_id: todo.goalId, - operation, + operation: operation === "resume" ? "edit" : operation, + ...(operation === "resume" ? {status: "open"} : {}), ...(operation === "block" ? { note: t("drawer.taskSuccessorNote") } : {}), ...(operation === "defer" && resumeWhen ? { resume_when: resumeWhen } : {}), todo_id: todo.todoId, @@ -432,6 +433,8 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention }); } + const todoAwaitingResume = selection.kind === "todo" && ["blocked", "deferred"].includes(selection.item.status ?? ""); + const todoCompleted = selection.kind === "todo" && (selection.item.status === "done" || selection.item.status == null && selection.item.done); const currentSubagentConfiguration: WorkspaceGoalSubagentConfiguration = selection.kind === "goal" ? verifiedSubagentConfiguration ?? selection.item.subagentExecution @@ -642,14 +645,14 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention <>
- - {selection.item.done ? t("drawer.taskStatusCompleted") : selection.item.retryReadback ? t("drawer.resumePending") : selection.item.status === "deferred" ? t("drawer.taskStatusDeferred") : selection.item.status === "blocked" ? t("drawer.taskStatusBlocked") : t("drawer.taskStatusOpen")} + + {selection.item.status === "done" ? t("drawer.taskStatusCompleted") : selection.item.retryReadback ? t("drawer.resumePending") : selection.item.status === "deferred" ? t("drawer.taskStatusDeferred") : selection.item.status === "blocked" ? t("drawer.taskStatusBlocked") : t("drawer.taskStatusOpen")} {selection.item.priority ? {selection.item.priority} : null} {selection.item.taskClass === "advancement_task" ? t("drawer.taskAdvancement") : selection.item.taskClass ?? t("drawer.taskOrdinary")}
- {selection.item.status === "blocked" && !selection.item.done ?
+ {selection.item.status === "blocked" ?

{t("drawer.blockedReason")}

{selection.item.blockedReason ?? t("drawer.blockedReasonMissing")}

: null} @@ -659,7 +662,7 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
Goal
{selection.item.goalTitle}
{t("common.owner")}
{selection.item.claimedBy ? selection.item.ownerLabel ?? selection.item.claimedBy : t("drawer.notAssigned")}
-
{t("common.status")}
{selection.item.done ? t("drawer.taskStatusCompleted") : selection.item.retryReadback ? t("drawer.resumePending") : selection.item.status === "deferred" ? t("drawer.taskStatusDeferred") : selection.item.status === "blocked" ? t("drawer.taskStatusBlocked") : t("drawer.taskStatusOpen")}
+
{t("common.status")}
{selection.item.status === "done" ? t("drawer.taskStatusCompleted") : selection.item.retryReadback ? t("drawer.resumePending") : selection.item.status === "deferred" ? t("drawer.taskStatusDeferred") : selection.item.status === "blocked" ? t("drawer.taskStatusBlocked") : t("drawer.taskStatusOpen")}
{t("drawer.priority")}
{selection.item.priority ?? t("drawer.notSet")}
{selection.item.completedAt ?
{t("drawer.completedAt")}
: null} {selection.item.dependencies ?
{t("drawer.dependencies")}
{selection.item.dependencies.join(" · ") || t("common.none")}
: null} @@ -676,10 +679,10 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
{t("drawer.validationDigest")}
{selection.item.validationDigest}
{selection.item.validationRevisionActor ?
{t("drawer.validationRevisionActor")}
{selection.item.validationRevisionActor}
: null} : null} -
{t("drawer.nextTransition")}
{selection.item.nextTransition ?? (selection.item.done ? t("drawer.taskNextCompleted") : selection.item.resumeInvalidReason === "dependency_cycle" ? t("drawer.taskNextCycle") : selection.item.retryReadback ? t("drawer.taskNextDeferred") : selection.item.resumeReady ? t("drawer.taskNextResumeReady") : selection.item.status === "deferred" ? t("drawer.taskNextDeferred") : selection.item.status === "blocked" ? t("drawer.taskNextBlocked") : t("drawer.taskNextOpen"))}
+
{t("drawer.nextTransition")}
{selection.item.nextTransition ?? (selection.item.status === "done" ? t("drawer.taskNextCompleted") : selection.item.resumeInvalidReason === "dependency_cycle" ? t("drawer.taskNextCycle") : selection.item.retryReadback ? t("drawer.taskNextDeferred") : selection.item.resumeReady ? t("drawer.taskNextResumeReady") : selection.item.status === "deferred" ? t("drawer.taskNextDeferred") : selection.item.status === "blocked" ? t("drawer.taskNextBlocked") : t("drawer.taskNextOpen"))}
- {!readOnly && !selection.item.done ?
+ {!readOnly && selection.item.status !== "done" ?
{t("drawer.taskManage")}
@@ -706,12 +709,13 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention actionKind: "todo.update", context: {goal_id: selection.item.goalId, kind: "todo", todo_id: selection.item.todoId}, idempotencyKey: `workspace-todo-${selection.item.todoId}-priority-${todoPriority}-${Date.now().toString(36)}`, - normalizedParameters: {goal_id: selection.item.goalId, todo_id: selection.item.todoId, operation: "edit", + normalizedParameters: {goal_id: selection.item.goalId, todo_id: selection.item.todoId, + agent_id: selection.item.claimedBy ?? todoAgentId, operation: "edit", ...(todoPriority === "clear" ? {clear_priority: true} : {priority: todoPriority})}, summary: `${t("drawer.taskPriority")}: ${todoPriority === "clear" ? t("drawer.taskPriorityClear") : todoPriority}`, })} type="button">{t("timeline.review")} - {t("drawer.taskDeferUntil")} + {!todoAwaitingResume ? <>{t("drawer.taskDeferUntil")} + : null}
- {todoTransitions.map((transition) => ( + {todoTransitions.filter(transition => !todoAwaitingResume || transition.operation !== "block").map((transition) => ( ))}
- -
+ + {todoAwaitingResume ? {t("drawer.taskResumeHint")} :
{t("drawer.taskCompletionProof")}