@@ -1252,6 +1252,59 @@ describe("ClineProvider - Sticky Mode", () => {
12521252 expect ( postSpy ) . toHaveBeenCalled ( )
12531253 } )
12541254
1255+ it ( "stays silent when a newer mutation supersedes the aborted switch" , async ( ) => {
1256+ // Same typed double as the activation-failure rollback test above.
1257+ const mockTask = Object . assign (
1258+ { } as Task ,
1259+ {
1260+ taskId : "test-task-id" ,
1261+ taskMode : "code" ,
1262+ _taskMode : "code" ,
1263+ emit : vi . fn ( ) ,
1264+ saveClineMessages : vi . fn ( ) ,
1265+ clineMessages : [ ] ,
1266+ apiConversationHistory : [ ] ,
1267+ updateApiConfiguration : vi . fn ( ) ,
1268+ } as Partial < Task > ,
1269+ )
1270+ const historyItem : HistoryItem = {
1271+ id : "test-task-id" ,
1272+ ts : Date . now ( ) ,
1273+ task : "Test task" ,
1274+ mode : "code" ,
1275+ number : 1 ,
1276+ tokensIn : 0 ,
1277+ tokensOut : 0 ,
1278+ cacheWrites : 0 ,
1279+ cacheReads : 0 ,
1280+ totalCost : 0 ,
1281+ }
1282+ vi . spyOn ( provider . taskHistoryStore , "get" ) . mockReturnValue ( historyItem )
1283+ vi . spyOn ( provider , "updateTaskHistory" ) . mockResolvedValue ( [ ] )
1284+ await provider . saveViewState ( "mode" , "code" )
1285+ await provider . contextProxy . setValue ( "mode" , "code" )
1286+
1287+ // The abort lands after this switch's mode writes, and a newer mutation
1288+ // immediately moves the mode stores to its own mode: this switch's commit
1289+ // is superseded, so announcing it would clobber the newer owner - the
1290+ // emit/post tail must stay silent.
1291+ const emitSpy = vi . spyOn ( provider , "emit" )
1292+ const postSpy = vi . spyOn ( provider , "postStateToWebview" ) . mockResolvedValue ( undefined )
1293+ const controller = new AbortController ( )
1294+ vi . spyOn ( provider . providerSettingsManager , "getModeConfigId" ) . mockImplementation ( async ( ) => {
1295+ controller . abort ( )
1296+ await provider . setValue ( "mode" , "ask" )
1297+ return undefined
1298+ } )
1299+ vi . spyOn ( provider . providerSettingsManager , "listConfig" ) . mockResolvedValue ( [ ] )
1300+ await provider . addClineToStack ( mockTask )
1301+
1302+ await provider [ "handleModeSwitchUnlocked" ] ( "architect" , mockTask , controller . signal )
1303+
1304+ expect ( provider . getValue ( "mode" ) ) . toBe ( "ask" )
1305+ expect ( emitSpy ) . not . toHaveBeenCalledWith ( "modeChanged" , "architect" )
1306+ expect ( postSpy ) . not . toHaveBeenCalled ( )
1307+ } )
12551308 it ( "keeps the cancellation result when the abort rollback write itself fails" , async ( ) => {
12561309 const mockTask = Object . assign (
12571310 { } as Task ,
0 commit comments