From 8aca1d8d7f59d2dab704abcc10bd69a5b8c9f323 Mon Sep 17 00:00:00 2001 From: Martin Meredith Date: Tue, 10 Nov 2026 14:16:59 +0000 Subject: [PATCH 1/2] fix(web,mobile): thread-bound scheduled tasks no longer offer a model their runs ignore A scheduled task bound to a thread runs on that thread's current model. The saved selection only seeds new threads, but the editor's Model field stayed editable, so a change saved and then did nothing. The field is now disabled while the draft is bound, with a note saying runs use the thread's model. Unbinding enables it again. Co-Authored-By: Claude --- .../SettingsScheduledTasksRouteScreen.tsx | 9 +++++++-- .../ScheduledTasksSettings.browser.test.tsx | 16 ++++++++++++++++ .../settings/ScheduledTasksSettings.tsx | 5 ++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx index ef4b696c82ec8..7db9d48e6da4a 100644 --- a/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx @@ -858,7 +858,10 @@ function TaskForm({ {promptField} - + navigation.navigate("SettingsScheduledTaskModel")} - disabled={saving || dictationPending || environmentUnavailable} + disabled={ + saving || dictationPending || environmentUnavailable || draft.threadId !== null + } /> diff --git a/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx b/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx index ca8192dd894e3..59019fe85bccd 100644 --- a/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx +++ b/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx @@ -215,4 +215,20 @@ describe("ScheduledTasksSettings", () => { await expect.element(dialog.getByRole("heading", { name: "New task" })).toBeInTheDocument(); await expect.element(name).toHaveValue("My draft"); }); + + it("locks the model while the task is bound to a thread", async () => { + setup(); + await using _screen = await render(); + const dialog = page.getByRole("dialog"); + const model = dialog.getByRole("button", { name: "Choose model" }); + const note = dialog.getByText("Runs use this thread's current model."); + await expect.element(model).toBeDisabled(); + await expect.element(note).toBeVisible(); + + await userEvent.click(dialog.getByRole("combobox", { name: "Destination" })); + await userEvent.click(page.getByRole("option", { name: "New thread each run" })); + + await expect.element(model).toBeEnabled(); + await expect.element(note).not.toBeInTheDocument(); + }); }); diff --git a/apps/web/src/components/settings/ScheduledTasksSettings.tsx b/apps/web/src/components/settings/ScheduledTasksSettings.tsx index ff024e5ba2ed2..412bc9a43be47 100644 --- a/apps/web/src/components/settings/ScheduledTasksSettings.tsx +++ b/apps/web/src/components/settings/ScheduledTasksSettings.tsx @@ -1786,7 +1786,7 @@ function ScheduledTaskEditorForm({
+ {draft.threadId !== "" ? ( +

Runs use this thread's current model.

+ ) : null}
From ef47208f6e4b84516b033bf1dba6b20d5301800b Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 13 Nov 2026 07:19:56 +0000 Subject: [PATCH 2/2] fix(web,mobile): hide the scheduled-task model while bound to a thread The disabled picker still showed the saved model, which bound runs ignore, and its note repeated the destination hint. Hide the field instead and let the destination text say that runs use the thread's current model. Co-Authored-By: Claude Opus 5.5 --- .../SettingsScheduledTasksRouteScreen.tsx | 43 +++++++++---------- .../ScheduledTasksSettings.browser.test.tsx | 10 ++--- .../settings/ScheduledTasksSettings.tsx | 43 +++++++++---------- docs/user/project-settings.md | 3 +- 4 files changed, 49 insertions(+), 50 deletions(-) diff --git a/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx index 767f90fcb9479..dd979f3855cbc 100644 --- a/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx @@ -858,10 +858,7 @@ function TaskForm({ {promptField} - + - - option.selection.instanceId === draft.modelSelection?.instanceId && - option.selection.model === draft.modelSelection?.model, - )?.label ?? - draft.modelSelection?.model ?? - (modelOptions.length ? "Choose model" : "No models available") - } - onPress={() => navigation.navigate("SettingsScheduledTaskModel")} - disabled={ - saving || dictationPending || environmentUnavailable || draft.threadId !== null - } - /> + {draft.threadId === null ? ( + + option.selection.instanceId === draft.modelSelection?.instanceId && + option.selection.model === draft.modelSelection?.model, + )?.label ?? + draft.modelSelection?.model ?? + (modelOptions.length ? "Choose model" : "No models available") + } + onPress={() => navigation.navigate("SettingsScheduledTaskModel")} + disabled={saving || dictationPending || environmentUnavailable} + /> + ) : null} diff --git a/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx b/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx index 59019fe85bccd..14f96b43ef850 100644 --- a/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx +++ b/apps/web/src/components/settings/ScheduledTasksSettings.browser.test.tsx @@ -216,19 +216,19 @@ describe("ScheduledTasksSettings", () => { await expect.element(name).toHaveValue("My draft"); }); - it("locks the model while the task is bound to a thread", async () => { + it("offers the model only while the task is not bound to a thread", async () => { setup(); await using _screen = await render(); const dialog = page.getByRole("dialog"); const model = dialog.getByRole("button", { name: "Choose model" }); - const note = dialog.getByText("Runs use this thread's current model."); - await expect.element(model).toBeDisabled(); - await expect.element(note).toBeVisible(); + await expect + .element(dialog.getByText("Each run continues this conversation", { exact: false })) + .toBeVisible(); + await expect.element(model).not.toBeInTheDocument(); await userEvent.click(dialog.getByRole("combobox", { name: "Destination" })); await userEvent.click(page.getByRole("option", { name: "New thread each run" })); await expect.element(model).toBeEnabled(); - await expect.element(note).not.toBeInTheDocument(); }); }); diff --git a/apps/web/src/components/settings/ScheduledTasksSettings.tsx b/apps/web/src/components/settings/ScheduledTasksSettings.tsx index 5d6211a2700e4..464d8536a51d7 100644 --- a/apps/web/src/components/settings/ScheduledTasksSettings.tsx +++ b/apps/web/src/components/settings/ScheduledTasksSettings.tsx @@ -1245,7 +1245,7 @@ function ScheduledTaskDestination({ : "The first run starts a thread with the workspace and model below. Later runs post there, each in a fresh session without earlier runs' context." : threadMode === "continue" ? "Each run continues this conversation, in its workspace and with its current model." - : "Each run starts a fresh session in this thread, without earlier runs' context."; + : "Each run starts a fresh session in this thread, with its current model and without earlier runs' context."; return ( {onThreadModeChange ? ( @@ -1766,27 +1766,26 @@ function ScheduledTaskEditorForm({ /> - -
- - setDraft((current) => ({ ...current, modelKey: `${instanceId}:${model}` })) - } - /> -
- {draft.threadId !== "" ? ( -

Runs use this thread's current model.

- ) : null} -
+ {draft.threadId === "" ? ( + +
+ + setDraft((current) => ({ ...current, modelKey: `${instanceId}:${model}` })) + } + /> +
+
+ ) : null}
{task?.schedule.type === "interval" && diff --git a/docs/user/project-settings.md b/docs/user/project-settings.md index e0377c11938a5..f65875dca4946 100644 --- a/docs/user/project-settings.md +++ b/docs/user/project-settings.md @@ -90,7 +90,8 @@ Under **Destination**, choose where runs go. **New thread each run** keeps every run separate. The two one-thread choices start a thread on the first run and post later runs there: one continues the same conversation, the other starts each run in a fresh session that doesn't see earlier runs, so the thread stays readable -without the agent's context growing. To move a one-thread task, pick another +without the agent's context growing. Once the thread exists, runs use its current +model, so the task no longer offers a model choice. To move a one-thread task, pick another thread in its project under **Destination**. Moving such a task to another project starts a new thread there on its next run.