@@ -3581,6 +3581,98 @@ describe("ChatView timeline estimator parity (full app)", () => {
35813581 }
35823582 } ) ;
35833583
3584+ it ( "creates a fresh worktree draft from an existing worktree thread when the default mode is worktree" , async ( ) => {
3585+ const mounted = await mountChatView ( {
3586+ viewport : DEFAULT_VIEWPORT ,
3587+ snapshot : {
3588+ ...createSnapshotForTargetUser ( {
3589+ targetMessageId : "msg-user-new-thread-worktree-default-test" as MessageId ,
3590+ targetText : "new thread worktree default test" ,
3591+ } ) ,
3592+ threads : createSnapshotForTargetUser ( {
3593+ targetMessageId : "msg-user-new-thread-worktree-default-test" as MessageId ,
3594+ targetText : "new thread worktree default test" ,
3595+ } ) . threads . map ( ( thread ) =>
3596+ thread . id === THREAD_ID
3597+ ? Object . assign ( { } , thread , {
3598+ branch : "feature/existing" ,
3599+ worktreePath : "/repo/.t3/worktrees/existing" ,
3600+ } )
3601+ : thread ,
3602+ ) ,
3603+ } ,
3604+ configureFixture : ( nextFixture ) => {
3605+ nextFixture . serverConfig = {
3606+ ...nextFixture . serverConfig ,
3607+ settings : {
3608+ ...nextFixture . serverConfig . settings ,
3609+ defaultThreadEnvMode : "worktree" ,
3610+ } ,
3611+ } ;
3612+ } ,
3613+ } ) ;
3614+
3615+ try {
3616+ const newThreadButton = page . getByTestId ( "new-thread-button" ) ;
3617+ await expect . element ( newThreadButton ) . toBeInTheDocument ( ) ;
3618+
3619+ await newThreadButton . click ( ) ;
3620+
3621+ const newThreadPath = await waitForURL (
3622+ mounted . router ,
3623+ ( path ) => UUID_ROUTE_RE . test ( path ) ,
3624+ "Route should change to a new draft thread." ,
3625+ ) ;
3626+ const newDraftId = draftIdFromPath ( newThreadPath ) ;
3627+
3628+ expect ( useComposerDraftStore . getState ( ) . getDraftSession ( newDraftId ) ) . toMatchObject ( {
3629+ envMode : "worktree" ,
3630+ worktreePath : null ,
3631+ } ) ;
3632+ } finally {
3633+ await mounted . cleanup ( ) ;
3634+ }
3635+ } ) ;
3636+
3637+ it ( "creates a new draft instead of reusing a promoting draft thread" , async ( ) => {
3638+ const mounted = await mountChatView ( {
3639+ viewport : DEFAULT_VIEWPORT ,
3640+ snapshot : createSnapshotForTargetUser ( {
3641+ targetMessageId : "msg-user-promoting-draft-new-thread-test" as MessageId ,
3642+ targetText : "promoting draft new thread test" ,
3643+ } ) ,
3644+ } ) ;
3645+
3646+ try {
3647+ const newThreadButton = page . getByTestId ( "new-thread-button" ) ;
3648+ await expect . element ( newThreadButton ) . toBeInTheDocument ( ) ;
3649+
3650+ await newThreadButton . click ( ) ;
3651+
3652+ const firstDraftPath = await waitForURL (
3653+ mounted . router ,
3654+ ( path ) => UUID_ROUTE_RE . test ( path ) ,
3655+ "Route should change to the first draft thread." ,
3656+ ) ;
3657+ const firstDraftId = draftIdFromPath ( firstDraftPath ) ;
3658+ const firstThreadId = draftThreadIdFor ( firstDraftId ) ;
3659+
3660+ await materializePromotedDraftThreadViaDomainEvent ( firstThreadId ) ;
3661+ expect ( mounted . router . state . location . pathname ) . toBe ( firstDraftPath ) ;
3662+
3663+ await newThreadButton . click ( ) ;
3664+
3665+ const secondDraftPath = await waitForURL (
3666+ mounted . router ,
3667+ ( path ) => UUID_ROUTE_RE . test ( path ) && path !== firstDraftPath ,
3668+ "Route should change to a second draft thread instead of reusing the promoting draft." ,
3669+ ) ;
3670+ expect ( draftIdFromPath ( secondDraftPath ) ) . not . toBe ( firstDraftId ) ;
3671+ } finally {
3672+ await mounted . cleanup ( ) ;
3673+ }
3674+ } ) ;
3675+
35843676 it ( "snapshots sticky codex settings into a new draft thread" , async ( ) => {
35853677 useComposerDraftStore . setState ( {
35863678 stickyModelSelectionByProvider : {
0 commit comments