Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 464edd8

Browse files
authored
feat(mobile): default new sessions to steer (port #3983)
Mobile is cloud-only, so the single messaging-mode default now starts new sessions in steer. Existing installs migrate off the persisted "queue" default to "steer" once, preserving per-task overrides. Generated-By: PostHog Code Task-Id: 0caff5c0-5715-49a9-9004-da9dd1b80f2b
1 parent f4fec98 commit 464edd8

3 files changed

Lines changed: 72 additions & 11 deletions

File tree

apps/mobile/src/app/settings/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ export default function SettingsScreen() {
418418
/>
419419
<SettingsRow
420420
label="Messaging mode"
421-
description="What happens when you send while a turn is running"
421+
description="Mode new sessions start in. Steer applies messages mid-turn. Queue holds them until the turn ends."
422422
onPress={() => setMessagingModeSheetOpen(true)}
423423
rightSlot={
424424
<>
Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
11
import { beforeEach, describe, expect, it } from "vitest";
2-
import { useMessagingModeStore } from "./messagingModeStore";
2+
import {
3+
type MessagingMode,
4+
useMessagingModeStore,
5+
} from "./messagingModeStore";
36

47
const INITIAL_STATE = useMessagingModeStore.getState();
58

9+
const migrate = useMessagingModeStore.persist.getOptions().migrate as (
10+
persisted: unknown,
11+
version: number,
12+
) => {
13+
modesByTaskId: Record<string, MessagingMode>;
14+
defaultMode: MessagingMode;
15+
};
16+
617
describe("messagingModeStore", () => {
718
beforeEach(() => {
819
useMessagingModeStore.setState(
9-
{ ...INITIAL_STATE, modesByTaskId: {}, defaultMode: "queue" },
20+
{ ...INITIAL_STATE, modesByTaskId: {}, defaultMode: "steer" },
1021
true,
1122
);
1223
});
1324

14-
it("defaults to Queue", () => {
25+
it("defaults to Steer", () => {
1526
expect(useMessagingModeStore.getState().getEffectiveMode("t1")).toBe(
16-
"queue",
27+
"steer",
1728
);
1829
});
1930

2031
it("falls back to the global default when a task has no override", () => {
21-
useMessagingModeStore.getState().setDefaultMode("steer");
32+
useMessagingModeStore.getState().setDefaultMode("queue");
2233
expect(useMessagingModeStore.getState().getEffectiveMode("t1")).toBe(
23-
"steer",
34+
"queue",
2435
);
2536
});
2637

2738
it("prefers a per-task override over the global default", () => {
28-
useMessagingModeStore.getState().setDefaultMode("steer");
2939
useMessagingModeStore.getState().setMode("t1", "queue");
3040
expect(useMessagingModeStore.getState().getEffectiveMode("t1")).toBe(
3141
"queue",
@@ -37,9 +47,47 @@ describe("messagingModeStore", () => {
3747
});
3848

3949
it("treats an undefined taskId as the global default", () => {
40-
useMessagingModeStore.getState().setDefaultMode("steer");
50+
useMessagingModeStore.getState().setDefaultMode("queue");
4151
expect(useMessagingModeStore.getState().getEffectiveMode(undefined)).toBe(
42-
"steer",
52+
"queue",
4353
);
4454
});
55+
56+
describe("migration", () => {
57+
it.each([
58+
{
59+
name: "flips a pre-v1 queue default to steer",
60+
version: 0,
61+
from: "queue",
62+
expected: "steer",
63+
},
64+
{
65+
name: "does not clobber a queue default set at the current version",
66+
version: 1,
67+
from: "queue",
68+
expected: "queue",
69+
},
70+
{
71+
name: "leaves a pre-v1 steer default untouched",
72+
version: 0,
73+
from: "steer",
74+
expected: "steer",
75+
},
76+
] as const)("$name", ({ version, from, expected }) => {
77+
const migrated = migrate(
78+
{ modesByTaskId: {}, defaultMode: from },
79+
version,
80+
);
81+
expect(migrated.defaultMode).toBe(expected);
82+
});
83+
84+
it("preserves per-task overrides through migration", () => {
85+
const migrated = migrate(
86+
{ modesByTaskId: { t1: "queue", t2: "steer" }, defaultMode: "queue" },
87+
0,
88+
);
89+
expect(migrated.modesByTaskId).toEqual({ t1: "queue", t2: "steer" });
90+
expect(migrated.defaultMode).toBe("steer");
91+
});
92+
});
4593
});

apps/mobile/src/features/tasks/stores/messagingModeStore.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const useMessagingModeStore = create<MessagingModeState>()(
1717
persist(
1818
(set, get) => ({
1919
modesByTaskId: {},
20-
defaultMode: "queue",
20+
defaultMode: "steer",
2121
setMode: (taskId, mode) =>
2222
set((state) => ({
2323
modesByTaskId: { ...state.modesByTaskId, [taskId]: mode },
@@ -34,6 +34,19 @@ export const useMessagingModeStore = create<MessagingModeState>()(
3434
{
3535
name: "messaging-mode-storage",
3636
storage: createJSONStorage(() => AsyncStorage),
37+
version: 1,
38+
// Pre-v1 installs persisted the old "queue" default, so flip them to the
39+
// new "steer" default once. Per-task overrides are left untouched.
40+
migrate: (persisted, version) => {
41+
const state = persisted as Pick<
42+
MessagingModeState,
43+
"modesByTaskId" | "defaultMode"
44+
>;
45+
if (version < 1 && state.defaultMode === "queue") {
46+
return { ...state, defaultMode: "steer" };
47+
}
48+
return state;
49+
},
3750
partialize: (state) => ({
3851
modesByTaskId: state.modesByTaskId,
3952
defaultMode: state.defaultMode,

0 commit comments

Comments
 (0)