| author | |
| committer | |
| log | f0c6ba994670bbd9eeab97eab6701d5c3589178d |
| tree | e1f5c75380c74e109cee50e2591676c6a0a3c8ce |
| parent | 90f3913a81380af7aeafac62f8132aeb98345a12 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>3 files changed, 20 insertions(+), 24 deletions(-)
apps/web/src/composerDraftStore.test.ts+8-4| ... | ... | @@ -1103,13 +1103,14 @@ describe("composerDraftStore project draft thread mapping", () => { |
| 1103 | 1103 | }); |
| 1104 | 1104 | }); |
| 1105 | 1105 | |
| 1106 | it("clears branch and worktree context when remapping a draft to another environment", () => { | |
| 1106 | it("clears branch and worktree but keeps env mode when remapping a draft to another environment", () => { | |
| 1107 | 1107 | const store = useComposerDraftStore.getState(); |
| 1108 | 1108 | store.setProjectDraftThreadId(projectRef, draftId, { |
| 1109 | 1109 | threadId, |
| 1110 | 1110 | branch: "feature/local-only", |
| 1111 | 1111 | worktreePath: "/tmp/local-worktree", |
| 1112 | 1112 | envMode: "worktree", |
| 1113 | startFromOrigin: true, | |
| 1113 | 1114 | }); |
| 1114 | 1115 | |
| 1115 | 1116 | store.setLogicalProjectDraftThreadId(scopedProjectKey(projectRef), remoteProjectRef, draftId, { |
| ... | ... | @@ -1121,17 +1122,19 @@ describe("composerDraftStore project draft thread mapping", () => { |
| 1121 | 1122 | projectId, |
| 1122 | 1123 | branch: null, |
| 1123 | 1124 | worktreePath: null, |
| 1124 | envMode: "local", | |
| 1125 | envMode: "worktree", | |
| 1126 | startFromOrigin: true, | |
| 1125 | 1127 | }); |
| 1126 | 1128 | }); |
| 1127 | 1129 | |
| 1128 | it("clears branch and worktree context when changing a draft thread project ref", () => { | |
| 1130 | it("clears branch and worktree but keeps env mode when changing a draft thread project ref", () => { | |
| 1129 | 1131 | const store = useComposerDraftStore.getState(); |
| 1130 | 1132 | store.setProjectDraftThreadId(projectRef, draftId, { |
| 1131 | 1133 | threadId, |
| 1132 | 1134 | branch: "feature/local-only", |
| 1133 | 1135 | worktreePath: "/tmp/local-worktree", |
| 1134 | 1136 | envMode: "worktree", |
| 1137 | startFromOrigin: true, | |
| 1135 | 1138 | }); |
| 1136 | 1139 | |
| 1137 | 1140 | store.setDraftThreadContext(draftId, { |
| ... | ... | @@ -1143,7 +1146,8 @@ describe("composerDraftStore project draft thread mapping", () => { |
| 1143 | 1146 | projectId, |
| 1144 | 1147 | branch: null, |
| 1145 | 1148 | worktreePath: null, |
| 1146 | envMode: "local", | |
| 1149 | envMode: "worktree", | |
| 1150 | startFromOrigin: true, | |
| 1147 | 1151 | }); |
| 1148 | 1152 | }); |
| 1149 | 1153 | }); |
apps/web/src/composerDraftStore.ts+11-18| ... | ... | @@ -1340,6 +1340,10 @@ function createDraftThreadState( |
| 1340 | 1340 | interactionMode?: ProviderInteractionMode; |
| 1341 | 1341 | }, |
| 1342 | 1342 | ): DraftThreadState { |
| 1343 | // A project change (including switching environments within a logical | |
| 1344 | // project) invalidates machine-specific context: the branch may not exist | |
| 1345 | // there and the worktree path certainly doesn't. The user's *intent* — | |
| 1346 | // env mode and start-from-origin — is machine-independent and carries. | |
| 1343 | 1347 | const projectChanged = |
| 1344 | 1348 | existingThread !== undefined && |
| 1345 | 1349 | (existingThread.environmentId !== projectRef.environmentId || |
| ... | ... | @@ -1358,9 +1362,7 @@ function createDraftThreadState( |
| 1358 | 1362 | : (options.branch ?? null); |
| 1359 | 1363 | const nextStartFromOrigin = |
| 1360 | 1364 | options?.startFromOrigin === undefined |
| 1361 | ? projectChanged | |
| 1362 | ? false | |
| 1363 | : (existingThread?.startFromOrigin ?? false) | |
| 1365 | ? (existingThread?.startFromOrigin ?? false) | |
| 1364 | 1366 | : options.startFromOrigin; |
| 1365 | 1367 | return { |
| 1366 | 1368 | threadId, |
| ... | ... | @@ -1374,12 +1376,7 @@ function createDraftThreadState( |
| 1374 | 1376 | branch: nextBranch, |
| 1375 | 1377 | worktreePath: nextWorktreePath, |
| 1376 | 1378 | envMode: |
| 1377 | options?.envMode ?? | |
| 1378 | (nextWorktreePath | |
| 1379 | ? "worktree" | |
| 1380 | : projectChanged | |
| 1381 | ? "local" | |
| 1382 | : (existingThread?.envMode ?? "local")), | |
| 1379 | options?.envMode ?? (nextWorktreePath ? "worktree" : (existingThread?.envMode ?? "local")), | |
| 1383 | 1380 | startFromOrigin: nextStartFromOrigin, |
| 1384 | 1381 | promotedTo: null, |
| 1385 | 1382 | }; |
| ... | ... | @@ -2343,6 +2340,9 @@ const composerDraftStore = create<ComposerDraftStoreState>()( |
| 2343 | 2340 | ) { |
| 2344 | 2341 | return state; |
| 2345 | 2342 | } |
| 2343 | // Mirrors createDraftThreadState: a project/environment change | |
| 2344 | // drops machine-specific context (branch, worktree path) but | |
| 2345 | // keeps the user's env mode and start-from-origin intent. | |
| 2346 | 2346 | const projectChanged = |
| 2347 | 2347 | nextProjectRef.environmentId !== existing.environmentId || |
| 2348 | 2348 | nextProjectRef.projectId !== existing.projectId; |
| ... | ... | @@ -2360,9 +2360,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()( |
| 2360 | 2360 | : (options.branch ?? null); |
| 2361 | 2361 | const nextStartFromOrigin = |
| 2362 | 2362 | options.startFromOrigin === undefined |
| 2363 | ? projectChanged | |
| 2364 | ? false | |
| 2365 | : existing.startFromOrigin | |
| 2363 | ? existing.startFromOrigin | |
| 2366 | 2364 | : options.startFromOrigin; |
| 2367 | 2365 | const nextDraftThread: DraftThreadState = { |
| 2368 | 2366 | threadId: existing.threadId, |
| ... | ... | @@ -2378,12 +2376,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()( |
| 2378 | 2376 | branch: nextBranch, |
| 2379 | 2377 | worktreePath: nextWorktreePath, |
| 2380 | 2378 | envMode: |
| 2381 | options.envMode ?? | |
| 2382 | (nextWorktreePath | |
| 2383 | ? "worktree" | |
| 2384 | : projectChanged | |
| 2385 | ? "local" | |
| 2386 | : (existing.envMode ?? "local")), | |
| 2379 | options.envMode ?? (nextWorktreePath ? "worktree" : (existing.envMode ?? "local")), | |
| 2387 | 2380 | startFromOrigin: nextStartFromOrigin, |
| 2388 | 2381 | promotedTo: existing.promotedTo ?? null, |
| 2389 | 2382 | }; |
apps/web/src/hooks/useHandleNewThread.ts+1-2| ... | ... | @@ -183,8 +183,7 @@ export function useNewThreadHandler() { |
| 183 | 183 | // The workspace context must also ride along here: when projectRef |
| 184 | 184 | // targets a different physical member of the logical project, |
| 185 | 185 | // createDraftThreadState treats the remap as a project change and |
| 186 | // would otherwise wipe branch/worktree and force "local" mode, | |
| 187 | // undoing the write above. | |
| 186 | // would otherwise wipe branch/worktree, undoing the write above. | |
| 188 | 187 | setLogicalProjectDraftThreadId( |
| 189 | 188 | logicalProjectKey, |
| 190 | 189 | projectRef, |