From 10167b8dbab2b38d6e66d310c68088bf99837b4b Mon Sep 17 00:00:00 2001 From: MocA-Love <64681295+MocA-Love@users.noreply.github.com> Date: Fri, 17 Apr 2026 05:25:38 +0900 Subject: [PATCH] =?UTF-8?q?feat(desktop):=20TodoManager=20preparing=20?= =?UTF-8?q?=E7=8A=B6=E6=85=8B=E3=81=A7=E3=82=82=20description/goal=20?= =?UTF-8?q?=E3=82=92=E7=B7=A8=E9=9B=86=E5=8F=AF=E8=83=BD=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `preparing` は supervisor が Claude プロセスを spawn する前の 準備状態で、この時点での編集は `prepareArtifacts` が goal.md を 書き直すため安全に反映される。キュー待ちで `preparing` 停滞中の セッションも編集できるようにする。 キュー待ちセッションの番が回ってきて編集中に `running` / `verifying` に遷移した場合は、フロント側の useEffect で編集モードを自動解除し toast で通知する。バックエンドも `queued` / `preparing` / `failed` / `aborted` / `escalated` のみ許可する二重ガードを維持。 Closes #232 --- .../src/main/todo-agent/trpc-router.ts | 14 +++++++------ .../todo-agent/TodoManager/TodoManager.tsx | 20 ++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/apps/desktop/src/main/todo-agent/trpc-router.ts b/apps/desktop/src/main/todo-agent/trpc-router.ts index 05e679a8929..d288606741e 100644 --- a/apps/desktop/src/main/todo-agent/trpc-router.ts +++ b/apps/desktop/src/main/todo-agent/trpc-router.ts @@ -220,11 +220,12 @@ export const createTodoAgentRouter = () => { /** * Edit the user-authored fields (description / goal) of a TODO - * session that has not started yet. Allowed only in pre-start - * states (queued / failed / aborted / escalated) so a running - * worker's prompt can never mutate under its feet. When the - * description / goal changes we rewrite the session's goal.md - * so the next run picks up the edit. + * session. Allowed in queued / preparing / failed / aborted / + * escalated. `preparing` is safe because the supervisor has + * not spawned Claude yet and `prepareArtifacts` will rewrite + * goal.md before it is read. Refused once the session is + * running / verifying so the worker's prompt never mutates + * under its feet. */ updateFields: publicProcedure .input( @@ -251,6 +252,7 @@ export const createTodoAgentRouter = () => { } if ( session.status !== "queued" && + session.status !== "preparing" && session.status !== "failed" && session.status !== "aborted" && session.status !== "escalated" @@ -258,7 +260,7 @@ export const createTodoAgentRouter = () => { throw new TRPCError({ code: "PRECONDITION_FAILED", message: - "実行中またはキュー済みでないセッションは編集できません。中断してから再度お試しください。", + "実行中のセッションは編集できません。中断してから再度お試しください。", }); } const patch: { diff --git a/apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx b/apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx index b807cb53a15..aa1d349f95b 100644 --- a/apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx +++ b/apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx @@ -1030,7 +1030,25 @@ function SessionDetail({ session, onDeleted }: SessionDetailProps) { } }, [invalidate, rerunMut, session.id]); - const canEditFields = canStart && !isRunning; + // `preparing` is still editable: the supervisor has not spawned + // Claude yet, and prepareArtifacts rewrites goal.md before Claude + // reads it, so an edit during preparing still takes effect. + const canEditFields = canStart || session.status === "preparing"; + + // Bail out of any in-flight edit the moment the session starts + // actually running — e.g. a queued session whose turn arrived + // mid-edit — so the user doesn't hit Save only to get rejected by + // the backend guard. Only cancel on running/verifying; terminal + // states stay editable (rerun path). + useEffect(() => { + if (!editingField) return; + if (session.status !== "running" && session.status !== "verifying") return; + setEditingField(null); + setEditDraft(""); + toast.warning( + "タスクの実行が開始されたため編集を中止しました。中断してから再度編集してください。", + ); + }, [editingField, session.status]); const startEditField = useCallback( (field: "description" | "goal") => {