diff --git a/.changeset/fn-7341-execution-mode-replan.md b/.changeset/fn-7341-execution-mode-replan.md new file mode 100644 index 0000000000..2e7dddd7f3 --- /dev/null +++ b/.changeset/fn-7341-execution-mode-replan.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Replan active tasks after confirming execution-mode changes. +category: fix +dev: Dashboard inline execution-mode changes on todo/in-progress tasks now confirm and call the spec rebuild path. diff --git a/packages/dashboard/app/components/TaskDetailModal.tsx b/packages/dashboard/app/components/TaskDetailModal.tsx index 738a0ba816..530710ff4d 100644 --- a/packages/dashboard/app/components/TaskDetailModal.tsx +++ b/packages/dashboard/app/components/TaskDetailModal.tsx @@ -398,6 +398,14 @@ function normalizeExecutionModeValue(executionMode: Task["executionMode"]): "sta return executionMode === "fast" ? "fast" : "standard"; } +function requiresExecutionModeReplan(column: Task["column"]): boolean { + /* + FNXC:ExecutionModeReplan 2026-06-30-00:00: + Todo and in-progress tasks can already hold a generated plan or active execution context. Changing Standard/Fast mode invalidates that plan, so the dashboard must confirm the change and send the task back through the existing replanning path instead of silently patching executionMode in place. + */ + return column === "todo" || column === "in-progress"; +} + interface ProvenanceDisplay { label: string; parentTaskId?: string; @@ -896,6 +904,10 @@ export function TaskDetailContent({ const [isSavingInlineExecutionMode, setIsSavingInlineExecutionMode] = useState(false); const [inlineNoCommitsExpected, setInlineNoCommitsExpected] = useState(task.noCommitsExpected === true); const [isSavingInlineNoCommitsExpected, setIsSavingInlineNoCommitsExpected] = useState(false); + const { confirm, confirmWithChoice, confirmWithCheckbox } = useConfirm(); + const requestClose = useCallback(() => { + onRequestClose?.(); + }, [onRequestClose]); const mountedRef = useRef(false); const activeTaskIdRef = useRef(task.id); @@ -1508,6 +1520,7 @@ export function TaskDetailContent({ const [editAutoSaveStatus, setEditAutoSaveStatus] = useState<"idle" | "saving" | "saved" | "error">("idle"); const editAutoSaveTimeoutRef = useRef | null>(null); const editAutoSaveRevisionRef = useRef(0); + const editSaveTriggeredReplanRef = useRef(false); const buildEditUpdates = useCallback((includeDescription: boolean) => { const updates: Record = {}; @@ -1601,20 +1614,47 @@ export function TaskDetailContent({ } return false; } + const replanAfterExecutionModeChange = Object.prototype.hasOwnProperty.call(updates, "executionMode") && requiresExecutionModeReplan(task.column); + if (replanAfterExecutionModeChange && !includeDescription) { + delete updates.executionMode; + } if (Object.keys(updates).length === 0) { return true; } + if (replanAfterExecutionModeChange && includeDescription) { + const nextMode = normalizeExecutionModeValue(updates.executionMode as Task["executionMode"]); + const shouldChangeMode = await confirm({ + title: t("taskDetail.executionMode.replanTitle", "Change execution mode and replan?"), + message: t("taskDetail.executionMode.replanMessage", "Changing execution mode for this task will move it back to Planning so Fusion can rebuild the plan for {{mode}} mode.", { mode: nextMode }), + }); + if (!shouldChangeMode) { + setEditExecutionMode(normalizeExecutionModeValue(task.executionMode)); + return false; + } + } const revision = ++editAutoSaveRevisionRef.current; setIsSaving(true); setEditAutoSaveStatus("saving"); try { const updatedTask = await updateTask(task.id, updates as never, projectId); if (revision !== editAutoSaveRevisionRef.current) return; + if (replanAfterExecutionModeChange && includeDescription) { + const normalizedUpdatedMode = normalizeExecutionModeValue(updatedTask.executionMode); + await rebuildTaskSpec(task.id, projectId); + editSaveTriggeredReplanRef.current = true; + setEditAutoSaveStatus("saved"); + requestClose(); + addToast(t("taskDetail.executionMode.replanning", "Execution mode updated to {{mode}} — {{id}} returned to Planning for replanning", { mode: normalizedUpdatedMode, id: task.id }), "info"); + return true; + } onTaskUpdated?.(updatedTask); setEditAutoSaveStatus("saved"); return true; } catch (err) { if (revision === editAutoSaveRevisionRef.current) { + if (replanAfterExecutionModeChange) { + setEditExecutionMode(normalizeExecutionModeValue(task.executionMode)); + } setEditAutoSaveStatus("error"); addToast(t("taskDetail.updateFailed", "Failed to update {{id}}: {{error}}", { id: task.id, error: getErrorMessage(err) }), "error"); } @@ -1624,15 +1664,16 @@ export function TaskDetailContent({ setIsSaving(false); } } - }, [addToast, buildEditUpdates, onTaskUpdated, projectId, task.id]); + }, [addToast, buildEditUpdates, confirm, onTaskUpdated, projectId, requestClose, task.column, task.executionMode, task.id]); const handleAutoSaveDescription = useCallback(async (_description: string) => { await persistEditChanges(true); }, [persistEditChanges]); const handleSave = useCallback(async () => { + editSaveTriggeredReplanRef.current = false; const didSave = await persistEditChanges(true); - if (!didSave) { + if (!didSave || editSaveTriggeredReplanRef.current) { return; } addToast(t("taskDetail.updateSuccess", "Updated {{id}}", { id: task.id }), "success"); @@ -1710,6 +1751,18 @@ export function TaskDetailContent({ const currentMode = normalizeExecutionModeValue(task.executionMode); const nextMode = currentMode === "fast" ? "standard" : "fast"; const previousMode = inlineExecutionMode; + const shouldReplan = requiresExecutionModeReplan(task.column); + + if (shouldReplan) { + const shouldChangeMode = await confirm({ + title: t("taskDetail.executionMode.replanTitle", "Change execution mode and replan?"), + message: t("taskDetail.executionMode.replanMessage", "Changing execution mode for this task will move it back to Planning so Fusion can rebuild the plan for {{mode}} mode.", { mode: nextMode }), + }); + if (!shouldChangeMode) { + setInlineExecutionMode(previousMode); + return; + } + } setInlineExecutionMode(nextMode); setIsSavingInlineExecutionMode(true); @@ -1717,6 +1770,12 @@ export function TaskDetailContent({ try { const updatedTask = await updateTask(task.id, { executionMode: nextMode === "fast" ? "fast" : null }, projectId); const normalizedUpdatedMode = normalizeExecutionModeValue(updatedTask.executionMode); + if (shouldReplan) { + await rebuildTaskSpec(task.id, projectId); + requestClose(); + addToast(t("taskDetail.executionMode.replanning", "Execution mode updated to {{mode}} — {{id}} returned to Planning for replanning", { mode: normalizedUpdatedMode, id: task.id }), "info"); + return; + } setInlineExecutionMode(normalizedUpdatedMode); onTaskUpdated?.(updatedTask); addToast(t("taskDetail.executionMode.updated", "Execution mode updated to {{mode}}", { mode: normalizedUpdatedMode }), "success"); @@ -1728,7 +1787,7 @@ export function TaskDetailContent({ setIsSavingInlineExecutionMode(false); } } - }, [task.id, task.executionMode, projectId, inlineExecutionMode, onTaskUpdated, addToast]); + }, [task.id, task.column, task.executionMode, projectId, inlineExecutionMode, onTaskUpdated, addToast, confirm, requestClose]); const handleInlineNoCommitsExpectedToggle = useCallback(async () => { const nextValue = !inlineNoCommitsExpected; @@ -1775,7 +1834,6 @@ export function TaskDetailContent({ const fileInputRef = useRef(null); const { nodes } = useNodes(); - const { confirm, confirmWithChoice, confirmWithCheckbox } = useConfirm(); const handleUnlinkGithubIssue = useCallback(async () => { if (!canEdit || !githubTrackedIssue || isSavingGithubTracking) return; @@ -1811,10 +1869,6 @@ export function TaskDetailContent({ activeTab === "chat" && activitySegment === "raw-logs", projectId, ); - const requestClose = useCallback(() => { - onRequestClose?.(); - }, [onRequestClose]); - useEffect(() => { if (embedded) return; const handleKey = (e: KeyboardEvent) => { diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx index ff3436eb3b..aecece7ea4 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx @@ -892,6 +892,40 @@ describe("TaskDetailModal", () => { }); }); + it("confirms and replans when edit-mode executionMode changes on a todo task", async () => { + const { updateTask, rebuildTaskSpec } = await import("../../api"); + const mockUpdate = vi.mocked(updateTask); + const mockRebuild = vi.mocked(rebuildTaskSpec); + mockUpdate.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "todo", title: "Test", description: "Desc", executionMode: null }) as Task); + mockRebuild.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "triage", status: "needs-replan", executionMode: null }) as Task); + + const { container } = render( + , + ); + + fireEvent.click(container.querySelector(".modal-edit-btn")!); + fireEvent.change(screen.getByTestId("task-form-execution-mode-select"), { target: { value: "standard" } }); + fireEvent.click(screen.getByText("Save")); + + await waitFor(() => { + expect(mockConfirm).toHaveBeenCalledWith(expect.objectContaining({ + title: "Change execution mode and replan?", + })); + expect(mockUpdate).toHaveBeenCalledWith("FN-001", { executionMode: null }, undefined); + expect(mockRebuild).toHaveBeenCalledWith("FN-001", undefined); + }); + expect(mockUpdate.mock.invocationCallOrder[0]).toBeLessThan(mockRebuild.mock.invocationCallOrder[0]); + }); + it("omits executionMode from update payload when unchanged", async () => { const { updateTask } = await import("../../api"); const mockUpdate = vi.mocked(updateTask); @@ -940,13 +974,13 @@ describe("TaskDetailModal", () => { const { updateTask } = await import("../../api"); const mockUpdate = vi.mocked(updateTask); mockUpdate - .mockResolvedValueOnce(makeTask({ id: "FN-001", column: "todo", priority: "urgent", executionMode: "standard" }) as Task) - .mockResolvedValueOnce(makeTask({ id: "FN-001", column: "todo", priority: "urgent", executionMode: "fast" }) as Task); + .mockResolvedValueOnce(makeTask({ id: "FN-001", column: "triage", priority: "urgent", executionMode: "standard" }) as Task) + .mockResolvedValueOnce(makeTask({ id: "FN-001", column: "triage", priority: "urgent", executionMode: "fast" }) as Task); render( { expect(addToast).toHaveBeenCalledWith("Failed to update FN-001: Request failed", "error"); }); - it("toggles inline execution mode from standard to fast", async () => { - const { updateTask } = await import("../../api"); + it("prompts before replanning a todo task when changing inline execution mode from standard to fast", async () => { + const { updateTask, rebuildTaskSpec } = await import("../../api"); const mockUpdate = vi.mocked(updateTask); + const mockRebuild = vi.mocked(rebuildTaskSpec); const addToast = vi.fn(); const onTaskUpdated = vi.fn(); - const updatedTask = makeTask({ id: "FN-001", column: "todo", executionMode: "fast" }); - mockUpdate.mockResolvedValueOnce(updatedTask as Task); + mockUpdate.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "todo", executionMode: "fast" }) as Task); + mockRebuild.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "triage", status: "needs-replan", executionMode: "fast" }) as Task); render( { />, ); - const toggle = screen.getByRole("button", { name: "Execution mode: standard" }); - fireEvent.click(toggle); + fireEvent.click(screen.getByRole("button", { name: "Execution mode: standard" })); + await waitFor(() => { + expect(mockConfirm).toHaveBeenCalledWith(expect.objectContaining({ + title: "Change execution mode and replan?", + message: "Changing execution mode for this task will move it back to Planning so Fusion can rebuild the plan for fast mode.", + })); + }); await waitFor(() => { expect(mockUpdate).toHaveBeenCalledWith("FN-001", { executionMode: "fast" }, undefined); + expect(mockRebuild).toHaveBeenCalledWith("FN-001", undefined); }); - expect(onTaskUpdated).toHaveBeenCalledWith(updatedTask); - expect(addToast).toHaveBeenCalledWith("Execution mode updated to fast", "success"); - await waitFor(() => { - expect(screen.getByRole("button", { name: "Execution mode: fast" })).toHaveAttribute("aria-pressed", "true"); - }); + expect(mockUpdate.mock.invocationCallOrder[0]).toBeLessThan(mockRebuild.mock.invocationCallOrder[0]); + expect(onTaskUpdated).not.toHaveBeenCalled(); + expect(addToast).toHaveBeenCalledWith("Execution mode updated to fast — FN-001 returned to Planning for replanning", "info"); }); - it("toggles inline execution mode from fast to standard", async () => { - const { updateTask } = await import("../../api"); + it("cancels a todo inline execution mode change before update or replan", async () => { + const { updateTask, rebuildTaskSpec } = await import("../../api"); const mockUpdate = vi.mocked(updateTask); + const mockRebuild = vi.mocked(rebuildTaskSpec); + mockConfirm.mockResolvedValueOnce(false); + + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Execution mode: standard" })); + + await waitFor(() => { + expect(mockConfirm).toHaveBeenCalled(); + }); + expect(mockUpdate).not.toHaveBeenCalled(); + expect(mockRebuild).not.toHaveBeenCalled(); + expect(screen.getByRole("button", { name: "Execution mode: standard" })).toHaveAttribute("aria-pressed", "false"); + }); + + it("prompts and replans a todo task when changing inline execution mode from fast to standard", async () => { + const { updateTask, rebuildTaskSpec } = await import("../../api"); + const mockUpdate = vi.mocked(updateTask); + const mockRebuild = vi.mocked(rebuildTaskSpec); mockUpdate.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "todo", executionMode: null }) as Task); + mockRebuild.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "triage", status: "needs-replan", executionMode: null }) as Task); render( { fireEvent.click(screen.getByRole("button", { name: "Execution mode: fast" })); await waitFor(() => { + expect(mockConfirm).toHaveBeenCalledWith(expect.objectContaining({ + message: "Changing execution mode for this task will move it back to Planning so Fusion can rebuild the plan for standard mode.", + })); expect(mockUpdate).toHaveBeenCalledWith("FN-001", { executionMode: null }, undefined); + expect(mockRebuild).toHaveBeenCalledWith("FN-001", undefined); }); + expect(mockUpdate.mock.invocationCallOrder[0]).toBeLessThan(mockRebuild.mock.invocationCallOrder[0]); + }); + + it("prompts and replans an in-progress task when changing inline execution mode", async () => { + const { updateTask, rebuildTaskSpec } = await import("../../api"); + const mockUpdate = vi.mocked(updateTask); + const mockRebuild = vi.mocked(rebuildTaskSpec); + mockUpdate.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "in-progress", executionMode: "fast" }) as Task); + mockRebuild.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "triage", status: "needs-replan", executionMode: "fast" }) as Task); + + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Execution mode: standard" })); + await waitFor(() => { - expect(screen.getByRole("button", { name: "Execution mode: standard" })).toHaveAttribute("aria-pressed", "false"); + expect(mockConfirm).toHaveBeenCalled(); + expect(mockUpdate).toHaveBeenCalledWith("FN-001", { executionMode: "fast" }, undefined); + expect(mockRebuild).toHaveBeenCalledWith("FN-001", undefined); }); }); - it("reverts inline execution mode when save fails", async () => { - const { updateTask } = await import("../../api"); + it("updates triage inline execution mode without prompting or replanning", async () => { + const { updateTask, rebuildTaskSpec } = await import("../../api"); const mockUpdate = vi.mocked(updateTask); + const mockRebuild = vi.mocked(rebuildTaskSpec); const addToast = vi.fn(); - mockUpdate.mockRejectedValueOnce(new Error("Request failed")); + const onTaskUpdated = vi.fn(); + const updatedTask = makeTask({ id: "FN-001", column: "triage", executionMode: "fast" }); + mockUpdate.mockResolvedValueOnce(updatedTask as Task); + + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Execution mode: standard" })); + + await waitFor(() => { + expect(mockUpdate).toHaveBeenCalledWith("FN-001", { executionMode: "fast" }, undefined); + }); + expect(mockConfirm).not.toHaveBeenCalled(); + expect(mockRebuild).not.toHaveBeenCalled(); + expect(onTaskUpdated).toHaveBeenCalledWith(updatedTask); + expect(addToast).toHaveBeenCalledWith("Execution mode updated to fast", "success"); + await waitFor(() => { + expect(screen.getByRole("button", { name: "Execution mode: fast" })).toHaveAttribute("aria-pressed", "true"); + }); + }); + + it("reverts inline execution mode when active-task replan fails after update", async () => { + const { updateTask, rebuildTaskSpec } = await import("../../api"); + const mockUpdate = vi.mocked(updateTask); + const mockRebuild = vi.mocked(rebuildTaskSpec); + const addToast = vi.fn(); + mockUpdate.mockResolvedValueOnce(makeTask({ id: "FN-001", column: "todo", executionMode: "fast" }) as Task); + mockRebuild.mockRejectedValueOnce(new Error("Replan failed")); render( { await waitFor(() => { expect(mockUpdate).toHaveBeenCalledWith("FN-001", { executionMode: "fast" }, undefined); + expect(mockRebuild).toHaveBeenCalledWith("FN-001", undefined); }); await waitFor(() => { expect(screen.getByRole("button", { name: "Execution mode: standard" })).toHaveAttribute("aria-pressed", "false"); }); - expect(addToast).toHaveBeenCalledWith("Failed to update FN-001: Request failed", "error"); + expect(addToast).toHaveBeenCalledWith("Failed to update FN-001: Replan failed", "error"); }); it("disables inline execution mode toggle while save is in-flight", async () => { @@ -1183,7 +1324,7 @@ describe("TaskDetailModal", () => { render( { fetchTaskDetail: vi.fn().mockResolvedValue(makeTask()), fetchAgentLogs: vi.fn().mockResolvedValue([]), requestSpecRevision: vi.fn().mockResolvedValue({}), + rebuildTaskSpec: vi.fn().mockResolvedValue(makeTask({ column: "triage", status: "needs-replan" })), approvePlan: vi.fn().mockResolvedValue({}), rejectPlan: vi.fn().mockResolvedValue({}), duplicateTask: vi.fn().mockResolvedValue({}), diff --git a/packages/i18n/locales/en/app.json b/packages/i18n/locales/en/app.json index ff6e67517b..9687cf5cf3 100644 --- a/packages/i18n/locales/en/app.json +++ b/packages/i18n/locales/en/app.json @@ -7306,6 +7306,9 @@ "executionMode": { "ariaLabel": "Execution mode: {{mode}}", "fast": "Fast", + "replanMessage": "Changing execution mode for this task will move it back to Planning so Fusion can rebuild the plan for {{mode}} mode.", + "replanTitle": "Change execution mode and replan?", + "replanning": "Execution mode updated to {{mode}} — {{id}} returned to Planning for replanning", "standard": "Standard", "updated": "Execution mode updated to {{mode}}" }, diff --git a/packages/i18n/locales/es/app.json b/packages/i18n/locales/es/app.json index 3fb614e06b..f5b0de1381 100644 --- a/packages/i18n/locales/es/app.json +++ b/packages/i18n/locales/es/app.json @@ -7297,7 +7297,10 @@ "ariaLabel": "Modo de ejecución: {{mode}}", "fast": "Rápido", "standard": "Estándar", - "updated": "Modo de ejecución actualizado a {{mode}}" + "updated": "Modo de ejecución actualizado a {{mode}}", + "replanMessage": "Cambiar el modo de ejecución de esta tarea la devolverá a Planificación para que Fusion reconstruya el plan para el modo {{mode}}.", + "replanTitle": "¿Cambiar el modo de ejecución y replanificar?", + "replanning": "Modo de ejecución actualizado a {{mode}} — {{id}} volvió a Planificación para replanificación" }, "executionModeFast": "Rápido", "executionModeStandard": "Estándar", diff --git a/packages/i18n/locales/fr/app.json b/packages/i18n/locales/fr/app.json index b3bc1b36f0..517e3a70d3 100644 --- a/packages/i18n/locales/fr/app.json +++ b/packages/i18n/locales/fr/app.json @@ -7297,7 +7297,10 @@ "ariaLabel": "Mode d'exécution : {{mode}}", "fast": "Rapide", "standard": "Standard", - "updated": "Mode d'exécution mis à jour : {{mode}}" + "updated": "Mode d'exécution mis à jour : {{mode}}", + "replanMessage": "Changer le mode d'exécution de cette tâche la renverra en planification afin que Fusion reconstruise le plan pour le mode {{mode}}.", + "replanTitle": "Changer le mode d'exécution et replanifier ?", + "replanning": "Mode d'exécution mis à jour vers {{mode}} — {{id}} est revenue en planification pour replanification" }, "executionModeFast": "Rapide", "executionModeStandard": "Standard", diff --git a/packages/i18n/locales/ko/app.json b/packages/i18n/locales/ko/app.json index 56cfe80826..6964e96755 100644 --- a/packages/i18n/locales/ko/app.json +++ b/packages/i18n/locales/ko/app.json @@ -7297,7 +7297,10 @@ "ariaLabel": "실행 모드: {{mode}}", "fast": "빠름", "standard": "표준", - "updated": "실행 모드가 {{mode}}로 업데이트되었습니다" + "updated": "실행 모드가 {{mode}}로 업데이트되었습니다", + "replanMessage": "이 작업의 실행 모드를 변경하면 Fusion이 {{mode}} 모드용 계획을 다시 만들 수 있도록 작업이 계획으로 돌아갑니다.", + "replanTitle": "실행 모드를 변경하고 다시 계획할까요?", + "replanning": "실행 모드가 {{mode}}로 업데이트되었습니다 — {{id}}가 재계획을 위해 계획으로 돌아갔습니다" }, "executionModeFast": "빠름", "executionModeStandard": "표준", diff --git a/packages/i18n/locales/zh-CN/app.json b/packages/i18n/locales/zh-CN/app.json index 136ad75303..9497e69b4c 100644 --- a/packages/i18n/locales/zh-CN/app.json +++ b/packages/i18n/locales/zh-CN/app.json @@ -7297,7 +7297,10 @@ "ariaLabel": "执行模式:{{mode}}", "fast": "快速", "standard": "标准", - "updated": "执行模式已更新为 {{mode}}" + "updated": "执行模式已更新为 {{mode}}", + "replanMessage": "更改此任务的执行模式会将其移回规划,以便 Fusion 可以为 {{mode}} 模式重新生成计划。", + "replanTitle": "更改执行模式并重新规划?", + "replanning": "执行模式已更新为 {{mode}} — {{id}} 已返回规划以重新规划" }, "executionModeFast": "快速", "executionModeStandard": "标准", diff --git a/packages/i18n/locales/zh-TW/app.json b/packages/i18n/locales/zh-TW/app.json index 2ef63518cf..3a2b1a1ada 100644 --- a/packages/i18n/locales/zh-TW/app.json +++ b/packages/i18n/locales/zh-TW/app.json @@ -7297,7 +7297,10 @@ "ariaLabel": "執行模式:{{mode}}", "fast": "快速", "standard": "標準", - "updated": "執行模式已更新為 {{mode}}" + "updated": "執行模式已更新為 {{mode}}", + "replanMessage": "變更此任務的執行模式會將其移回規劃,讓 Fusion 可以為 {{mode}} 模式重新產生計畫。", + "replanTitle": "變更執行模式並重新規劃?", + "replanning": "執行模式已更新為 {{mode}} — {{id}} 已返回規劃以重新規劃" }, "executionModeFast": "快速", "executionModeStandard": "標準", diff --git a/packages/i18n/src/resources.d.ts b/packages/i18n/src/resources.d.ts index 1d1afa7332..bb7167a087 100644 --- a/packages/i18n/src/resources.d.ts +++ b/packages/i18n/src/resources.d.ts @@ -7345,6 +7345,9 @@ export default interface Resources { "executionMode": { "ariaLabel": "Execution mode: {{mode}}", "fast": "Fast", + "replanMessage": "Changing execution mode for this task will move it back to Planning so Fusion can rebuild the plan for {{mode}} mode.", + "replanTitle": "Change execution mode and replan?", + "replanning": "Execution mode updated to {{mode}} — {{id}} returned to Planning for replanning", "standard": "Standard", "updated": "Execution mode updated to {{mode}}" },