diff --git a/packages/ui/src/features/settings/settingsStore.test.ts b/packages/ui/src/features/settings/settingsStore.test.ts
index 26376f6504..208280e7bd 100644
--- a/packages/ui/src/features/settings/settingsStore.test.ts
+++ b/packages/ui/src/features/settings/settingsStore.test.ts
@@ -58,6 +58,7 @@ describe("feature settingsStore cloud selections", () => {
allowBypassPermissions: false,
lastUsedCloudRepository: null,
cachedCloudRepositoryMap: {},
+ cachedCloudDefaultBranchMap: {},
});
});
@@ -141,6 +142,63 @@ describe("feature settingsStore cloud selections", () => {
});
});
+ it("caches and persists the cloud default branch per repo", async () => {
+ useSettingsStore
+ .getState()
+ .setCachedCloudDefaultBranch("posthog/posthog", "master");
+ useSettingsStore
+ .getState()
+ .setCachedCloudDefaultBranch("posthog/code", "main");
+
+ expect(useSettingsStore.getState().cachedCloudDefaultBranchMap).toEqual({
+ "posthog/posthog": "master",
+ "posthog/code": "main",
+ });
+
+ await waitForPersistedWrite();
+
+ const lastCall = setItem.mock.calls[setItem.mock.calls.length - 1];
+ const persisted = JSON.parse(lastCall[1]);
+
+ expect(persisted.state.cachedCloudDefaultBranchMap).toEqual({
+ "posthog/posthog": "master",
+ "posthog/code": "main",
+ });
+ });
+
+ it("keeps the same map reference when the default branch is unchanged", () => {
+ useSettingsStore
+ .getState()
+ .setCachedCloudDefaultBranch("posthog/code", "main");
+ const first = useSettingsStore.getState().cachedCloudDefaultBranchMap;
+
+ useSettingsStore
+ .getState()
+ .setCachedCloudDefaultBranch("posthog/code", "main");
+ const second = useSettingsStore.getState().cachedCloudDefaultBranchMap;
+
+ expect(second).toBe(first);
+ });
+
+ it("rehydrates the cached cloud default branch map", async () => {
+ getItem.mockResolvedValue(
+ JSON.stringify({
+ state: {
+ cachedCloudDefaultBranchMap: { "posthog/code": "main" },
+ },
+ version: 0,
+ }),
+ );
+
+ useSettingsStore.setState({ cachedCloudDefaultBranchMap: {} });
+
+ await useSettingsStore.persist.rehydrate();
+
+ expect(useSettingsStore.getState().cachedCloudDefaultBranchMap).toEqual({
+ "posthog/code": "main",
+ });
+ });
+
it("rehydrates the unsafe mode toggle", async () => {
getItem.mockResolvedValue(
JSON.stringify({
diff --git a/packages/ui/src/features/settings/settingsStore.ts b/packages/ui/src/features/settings/settingsStore.ts
index 44914f4501..e8181760bb 100644
--- a/packages/ui/src/features/settings/settingsStore.ts
+++ b/packages/ui/src/features/settings/settingsStore.ts
@@ -107,6 +107,10 @@ interface SettingsStore {
lastUsedReasoningEffort: string | null;
lastUsedCloudRepository: string | null;
cachedCloudRepositoryMap: Record;
+ // Last-known default ("trunk") branch per cloud repo, keyed by lowercased
+ // "owner/repo". Persisted so a cold start can pre-select trunk in the branch
+ // picker immediately, before the (slow) live branch list resolves.
+ cachedCloudDefaultBranchMap: Record;
lastUsedEnvironments: Record;
defaultInitialTaskMode: DefaultInitialTaskMode;
lastUsedInitialTaskMode: ExecutionMode;
@@ -126,6 +130,7 @@ interface SettingsStore {
setCachedCloudRepositoryMap: (
map: Record,
) => void;
+ setCachedCloudDefaultBranch: (repo: string, branch: string) => void;
setLastUsedEnvironment: (
repoPath: string,
environmentId: string | null,
@@ -289,6 +294,7 @@ export const useSettingsStore = create()(
lastUsedReasoningEffort: null,
lastUsedCloudRepository: null,
cachedCloudRepositoryMap: {},
+ cachedCloudDefaultBranchMap: {},
lastUsedEnvironments: {},
defaultInitialTaskMode: "plan",
lastUsedInitialTaskMode: "plan",
@@ -308,6 +314,16 @@ export const useSettingsStore = create()(
set({ lastUsedCloudRepository: repo }),
setCachedCloudRepositoryMap: (map) =>
set({ cachedCloudRepositoryMap: map }),
+ setCachedCloudDefaultBranch: (repo, branch) =>
+ set((state) => {
+ if (state.cachedCloudDefaultBranchMap[repo] === branch) return {};
+ return {
+ cachedCloudDefaultBranchMap: {
+ ...state.cachedCloudDefaultBranchMap,
+ [repo]: branch,
+ },
+ };
+ }),
setLastUsedEnvironment: (repoPath, environmentId) =>
set((state) => {
const next = { ...state.lastUsedEnvironments };
@@ -498,6 +514,7 @@ export const useSettingsStore = create()(
lastUsedReasoningEffort: state.lastUsedReasoningEffort,
lastUsedCloudRepository: state.lastUsedCloudRepository,
cachedCloudRepositoryMap: state.cachedCloudRepositoryMap,
+ cachedCloudDefaultBranchMap: state.cachedCloudDefaultBranchMap,
lastUsedEnvironments: state.lastUsedEnvironments,
defaultInitialTaskMode: state.defaultInitialTaskMode,
lastUsedInitialTaskMode: state.lastUsedInitialTaskMode,
diff --git a/packages/ui/src/features/task-detail/components/TaskInput.tsx b/packages/ui/src/features/task-detail/components/TaskInput.tsx
index 1f7981cb2c..ac95290dca 100644
--- a/packages/ui/src/features/task-detail/components/TaskInput.tsx
+++ b/packages/ui/src/features/task-detail/components/TaskInput.tsx
@@ -189,6 +189,8 @@ export function TaskInput({
setLastUsedAdapter,
lastUsedCloudRepository,
setLastUsedCloudRepository,
+ cachedCloudDefaultBranchMap,
+ setCachedCloudDefaultBranch,
allowBypassPermissions,
setLastUsedEnvironment,
getLastUsedEnvironment,
@@ -430,7 +432,33 @@ export function TaskInput({
cloudBranchSearchQuery,
);
const cloudBranches = cloudBranchData?.branches;
- const cloudDefaultBranch = cloudBranchData?.defaultBranch ?? null;
+ const liveCloudDefaultBranch = cloudBranchData?.defaultBranch ?? null;
+ // Serve the persisted default branch until the live list resolves, so the
+ // majority "start on trunk" case pre-selects trunk with zero wait on a cold
+ // start. The cached value is best-effort: if it's stale (a default branch
+ // renamed since it was cached), `cloudDefaultBranch` switches to the live
+ // value on arrival and BranchSelector re-selects it — as long as the user
+ // hasn't picked a branch of their own in the meantime.
+ const cloudDefaultBranch =
+ liveCloudDefaultBranch ??
+ (selectedCloudRepository
+ ? (cachedCloudDefaultBranchMap[selectedCloudRepository] ?? null)
+ : null);
+
+ // Persist the freshly loaded default branch so the next cold start can
+ // pre-select trunk immediately.
+ useEffect(() => {
+ if (selectedCloudRepository && liveCloudDefaultBranch) {
+ setCachedCloudDefaultBranch(
+ selectedCloudRepository,
+ liveCloudDefaultBranch,
+ );
+ }
+ }, [
+ selectedCloudRepository,
+ liveCloudDefaultBranch,
+ setCachedCloudDefaultBranch,
+ ]);
const {
branchOpen,