Skip to content

Commit 2504d1d

Browse files
authored
feat(task-input): cache cloud default branch for instant "start on trunk"
Persist the last-known default ("trunk") branch per cloud repo so a cold start can pre-select trunk in the branch picker immediately, instead of waiting on the slow live branch list to resolve. The picker already auto-selects the default branch when nothing is selected; feeding it a cached value makes that fire with zero wait. The fresh value from the live query takes over the moment it arrives and is written back to the cache. Generated-By: PostHog Code Task-Id: 562fb90f-d823-425d-b53c-5f482b55a8b2
1 parent 4f27d54 commit 2504d1d

3 files changed

Lines changed: 101 additions & 1 deletion

File tree

packages/ui/src/features/settings/settingsStore.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ describe("feature settingsStore cloud selections", () => {
5858
allowBypassPermissions: false,
5959
lastUsedCloudRepository: null,
6060
cachedCloudRepositoryMap: {},
61+
cachedCloudDefaultBranchMap: {},
6162
});
6263
});
6364

@@ -141,6 +142,63 @@ describe("feature settingsStore cloud selections", () => {
141142
});
142143
});
143144

145+
it("caches and persists the cloud default branch per repo", async () => {
146+
useSettingsStore
147+
.getState()
148+
.setCachedCloudDefaultBranch("posthog/posthog", "master");
149+
useSettingsStore
150+
.getState()
151+
.setCachedCloudDefaultBranch("posthog/code", "main");
152+
153+
expect(useSettingsStore.getState().cachedCloudDefaultBranchMap).toEqual({
154+
"posthog/posthog": "master",
155+
"posthog/code": "main",
156+
});
157+
158+
await waitForPersistedWrite();
159+
160+
const lastCall = setItem.mock.calls[setItem.mock.calls.length - 1];
161+
const persisted = JSON.parse(lastCall[1]);
162+
163+
expect(persisted.state.cachedCloudDefaultBranchMap).toEqual({
164+
"posthog/posthog": "master",
165+
"posthog/code": "main",
166+
});
167+
});
168+
169+
it("keeps the same map reference when the default branch is unchanged", () => {
170+
useSettingsStore
171+
.getState()
172+
.setCachedCloudDefaultBranch("posthog/code", "main");
173+
const first = useSettingsStore.getState().cachedCloudDefaultBranchMap;
174+
175+
useSettingsStore
176+
.getState()
177+
.setCachedCloudDefaultBranch("posthog/code", "main");
178+
const second = useSettingsStore.getState().cachedCloudDefaultBranchMap;
179+
180+
expect(second).toBe(first);
181+
});
182+
183+
it("rehydrates the cached cloud default branch map", async () => {
184+
getItem.mockResolvedValue(
185+
JSON.stringify({
186+
state: {
187+
cachedCloudDefaultBranchMap: { "posthog/code": "main" },
188+
},
189+
version: 0,
190+
}),
191+
);
192+
193+
useSettingsStore.setState({ cachedCloudDefaultBranchMap: {} });
194+
195+
await useSettingsStore.persist.rehydrate();
196+
197+
expect(useSettingsStore.getState().cachedCloudDefaultBranchMap).toEqual({
198+
"posthog/code": "main",
199+
});
200+
});
201+
144202
it("rehydrates the unsafe mode toggle", async () => {
145203
getItem.mockResolvedValue(
146204
JSON.stringify({

packages/ui/src/features/settings/settingsStore.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ interface SettingsStore {
107107
lastUsedReasoningEffort: string | null;
108108
lastUsedCloudRepository: string | null;
109109
cachedCloudRepositoryMap: Record<string, UserRepositoryIntegrationRef>;
110+
// Last-known default ("trunk") branch per cloud repo, keyed by lowercased
111+
// "owner/repo". Persisted so a cold start can pre-select trunk in the branch
112+
// picker immediately, before the (slow) live branch list resolves.
113+
cachedCloudDefaultBranchMap: Record<string, string>;
110114
lastUsedEnvironments: Record<string, string>;
111115
defaultInitialTaskMode: DefaultInitialTaskMode;
112116
lastUsedInitialTaskMode: ExecutionMode;
@@ -126,6 +130,7 @@ interface SettingsStore {
126130
setCachedCloudRepositoryMap: (
127131
map: Record<string, UserRepositoryIntegrationRef>,
128132
) => void;
133+
setCachedCloudDefaultBranch: (repo: string, branch: string) => void;
129134
setLastUsedEnvironment: (
130135
repoPath: string,
131136
environmentId: string | null,
@@ -289,6 +294,7 @@ export const useSettingsStore = create<SettingsStore>()(
289294
lastUsedReasoningEffort: null,
290295
lastUsedCloudRepository: null,
291296
cachedCloudRepositoryMap: {},
297+
cachedCloudDefaultBranchMap: {},
292298
lastUsedEnvironments: {},
293299
defaultInitialTaskMode: "plan",
294300
lastUsedInitialTaskMode: "plan",
@@ -308,6 +314,16 @@ export const useSettingsStore = create<SettingsStore>()(
308314
set({ lastUsedCloudRepository: repo }),
309315
setCachedCloudRepositoryMap: (map) =>
310316
set({ cachedCloudRepositoryMap: map }),
317+
setCachedCloudDefaultBranch: (repo, branch) =>
318+
set((state) => {
319+
if (state.cachedCloudDefaultBranchMap[repo] === branch) return {};
320+
return {
321+
cachedCloudDefaultBranchMap: {
322+
...state.cachedCloudDefaultBranchMap,
323+
[repo]: branch,
324+
},
325+
};
326+
}),
311327
setLastUsedEnvironment: (repoPath, environmentId) =>
312328
set((state) => {
313329
const next = { ...state.lastUsedEnvironments };
@@ -498,6 +514,7 @@ export const useSettingsStore = create<SettingsStore>()(
498514
lastUsedReasoningEffort: state.lastUsedReasoningEffort,
499515
lastUsedCloudRepository: state.lastUsedCloudRepository,
500516
cachedCloudRepositoryMap: state.cachedCloudRepositoryMap,
517+
cachedCloudDefaultBranchMap: state.cachedCloudDefaultBranchMap,
501518
lastUsedEnvironments: state.lastUsedEnvironments,
502519
defaultInitialTaskMode: state.defaultInitialTaskMode,
503520
lastUsedInitialTaskMode: state.lastUsedInitialTaskMode,

packages/ui/src/features/task-detail/components/TaskInput.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ export function TaskInput({
189189
setLastUsedAdapter,
190190
lastUsedCloudRepository,
191191
setLastUsedCloudRepository,
192+
cachedCloudDefaultBranchMap,
193+
setCachedCloudDefaultBranch,
192194
allowBypassPermissions,
193195
setLastUsedEnvironment,
194196
getLastUsedEnvironment,
@@ -430,7 +432,30 @@ export function TaskInput({
430432
cloudBranchSearchQuery,
431433
);
432434
const cloudBranches = cloudBranchData?.branches;
433-
const cloudDefaultBranch = cloudBranchData?.defaultBranch ?? null;
435+
const liveCloudDefaultBranch = cloudBranchData?.defaultBranch ?? null;
436+
// Serve the persisted default branch until the live list resolves, so the
437+
// majority "start on trunk" case pre-selects trunk with zero wait on a cold
438+
// start. Falls through to the fresh value the moment it arrives.
439+
const cloudDefaultBranch =
440+
liveCloudDefaultBranch ??
441+
(selectedCloudRepository
442+
? (cachedCloudDefaultBranchMap[selectedCloudRepository] ?? null)
443+
: null);
444+
445+
// Persist the freshly loaded default branch so the next cold start can
446+
// pre-select trunk immediately.
447+
useEffect(() => {
448+
if (selectedCloudRepository && liveCloudDefaultBranch) {
449+
setCachedCloudDefaultBranch(
450+
selectedCloudRepository,
451+
liveCloudDefaultBranch,
452+
);
453+
}
454+
}, [
455+
selectedCloudRepository,
456+
liveCloudDefaultBranch,
457+
setCachedCloudDefaultBranch,
458+
]);
434459

435460
const {
436461
branchOpen,

0 commit comments

Comments
 (0)