diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 17eb6a80..f125d491 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -12,6 +12,9 @@
       
       
     
+    
+      
+    
     
       
       
diff --git a/build.gradle.kts b/build.gradle.kts
index c27fb309..c4b589df 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -157,6 +157,7 @@ allprojects {
     kotlinOptions {
       val version = JavaVersion.VERSION_11.toString()
       jvmTarget = version
+      freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
     }
   }
 
diff --git a/data/src/main/java/com/hoc/flowmvi/data/UserRepositoryImpl.kt b/data/src/main/java/com/hoc/flowmvi/data/UserRepositoryImpl.kt
index 9297e6c1..cf424dcd 100644
--- a/data/src/main/java/com/hoc/flowmvi/data/UserRepositoryImpl.kt
+++ b/data/src/main/java/com/hoc/flowmvi/data/UserRepositoryImpl.kt
@@ -2,10 +2,11 @@ package com.hoc.flowmvi.data
 
 import arrow.core.Either.Companion.catch as catchEither
 import arrow.core.ValidatedNel
-import arrow.core.continuations.either
+import arrow.core.continuations.EffectScope
 import arrow.core.left
 import arrow.core.leftWiden
 import arrow.core.right
+import arrow.core.traverse
 import arrow.core.valueOr
 import com.hoc.flowmvi.core.Mapper
 import com.hoc.flowmvi.core.dispatchers.AppCoroutineDispatchers
@@ -16,6 +17,7 @@ import com.hoc.flowmvi.domain.model.User
 import com.hoc.flowmvi.domain.model.UserError
 import com.hoc.flowmvi.domain.model.UserValidationError
 import com.hoc.flowmvi.domain.repository.UserRepository
+import com.hoc081098.flowext.flowFromSuspend
 import com.hoc081098.flowext.retryWithExponentialBackoff
 import java.io.IOException
 import kotlin.time.Duration.Companion.milliseconds
@@ -24,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.asFlow
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapConcat
@@ -51,34 +52,28 @@ internal class UserRepositoryImpl(
     class Added(val user: User) : Change()
   }
 
-  private val responseToDomainThrows: (UserResponse) -> User = { response ->
-    responseToDomain(response).let { validated ->
-      validated.valueOr {
-        val t = UserError.ValidationFailed(it.toSet())
-        logError(t, "Map $response to user")
-        throw t
-      }
-    }
-  }
-
   private val changesFlow = MutableSharedFlow(extraBufferCapacity = 64)
-
   private suspend inline fun sendChange(change: Change) = changesFlow.emit(change)
 
-  @Suppress("NOTHING_TO_INLINE")
-  private inline fun logError(t: Throwable, message: String) = Timber.tag(TAG).e(t, message)
-
-  private fun getUsersFromRemote(): Flow> = suspend {
+  private fun getUsersFromRemote(): Flow> = flowFromSuspend {
     Timber.d("[USER_REPO] getUsersFromRemote ...")
+
     userApiService
       .getUsers()
-      .map(responseToDomainThrows)
-  }.asFlow()
-    .retryWithExponentialBackoff(
-      maxAttempt = 2,
-      initialDelay = 500.milliseconds,
-      factor = 2.0,
-    ) { it is IOException }
+      .let { response ->
+        response
+          .traverse(responseToDomain)
+          .valueOr { validationErrors ->
+            throw UserError.ValidationFailed(validationErrors.toSet()).also {
+              logError(it, "Failed to map $response to user")
+            }
+          }
+      }
+  }.retryWithExponentialBackoff(
+    maxAttempt = 2,
+    initialDelay = 500.milliseconds,
+    factor = 2.0,
+  ) { it is IOException }
 
   override fun getUsers() = getUsersFromRemote()
     .flatMapConcat { initial ->
@@ -99,13 +94,16 @@ internal class UserRepositoryImpl(
       emit(errorMapper(it).left())
     }
 
+  context(EffectScope)
   override suspend fun refresh() = catchEither { getUsersFromRemote().first() }
     .tap { sendChange(Change.Refreshed(it)) }
     .map { }
     .tapLeft { logError(it, "refresh") }
     .mapLeft(errorMapper)
+    .bind()
 
-  override suspend fun remove(user: User) = either {
+  context(EffectScope)
+  override suspend fun remove(user: User) {
     withContext(dispatchers.io) {
       val response = catchEither { userApiService.remove(user.id) }
         .tapLeft { logError(it, "remove user=$user") }
@@ -121,7 +119,8 @@ internal class UserRepositoryImpl(
     }
   }
 
-  override suspend fun add(user: User) = either {
+  context(EffectScope)
+  override suspend fun add(user: User) {
     withContext(dispatchers.io) {
       val response = catchEither { userApiService.add(domainToBody(user)) }
         .tapLeft { logError(it, "add user=$user") }
@@ -137,13 +136,30 @@ internal class UserRepositoryImpl(
     }
   }
 
+  context(EffectScope)
   override suspend fun search(query: String) = withContext(dispatchers.io) {
-    catchEither { userApiService.search(query).map(responseToDomainThrows) }
+    val userResponses = catchEither { userApiService.search(query) }
       .tapLeft { logError(it, "search query=$query") }
       .mapLeft(errorMapper)
+      .bind()
+
+    val users = userResponses.traverse(responseToDomain)
+      .mapLeft { UserError.ValidationFailed(it.toSet()) }
+      .tapInvalid {
+        logError(
+          it,
+          "search query=$query, failed to map $userResponses to List"
+        )
+      }
+      .bind()
+
+    users
   }
 
   private companion object {
     private val TAG = UserRepositoryImpl::class.java.simpleName
+
+    @Suppress("NOTHING_TO_INLINE")
+    private inline fun logError(t: Throwable, message: String) = Timber.tag(TAG).e(t, message)
   }
 }
diff --git a/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt b/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt
index cd5fcf78..7ea5164c 100644
--- a/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt
+++ b/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt
@@ -2,6 +2,7 @@ package com.hoc.flowmvi.data
 
 import arrow.core.Either
 import arrow.core.ValidatedNel
+import arrow.core.continuations.either
 import arrow.core.validNel
 import com.hoc.flowmvi.core.Mapper
 import com.hoc.flowmvi.data.remote.UserApiService
@@ -144,7 +145,7 @@ class UserRepositoryImplTest {
     coEvery { userApiService.getUsers() } returns USER_RESPONSES
     every { responseToDomain(any()) } returnsMany VALID_NEL_USERS
 
-    val result = repo.refresh()
+    val result = either { repo.refresh() }
 
     assertTrue(result.isRight())
     assertNotNull(result.orNull())
@@ -163,7 +164,7 @@ class UserRepositoryImplTest {
     coEvery { userApiService.getUsers() } throws ioException
     every { errorMapper(ofType()) } returns UserError.NetworkError
 
-    val result = repo.refresh()
+    val result = either { repo.refresh() }
 
     assertTrue(result.isLeft())
     assertEquals(UserError.NetworkError, result.leftOrThrow)
@@ -179,7 +180,7 @@ class UserRepositoryImplTest {
     coEvery { userApiService.remove(user.id) } returns userResponse
     every { responseToDomain(userResponse) } returns user.validNel()
 
-    val result = repo.remove(user)
+    val result = either { repo.remove(user) }
 
     assertTrue(result.isRight())
     assertNotNull(result.orNull())
@@ -194,7 +195,7 @@ class UserRepositoryImplTest {
     coEvery { userApiService.remove(user.id) } throws IOException()
     every { errorMapper(ofType()) } returns UserError.NetworkError
 
-    val result = repo.remove(user)
+    val result = either { repo.remove(user) }
 
     assertTrue(result.isLeft())
     assertEquals(UserError.NetworkError, result.leftOrThrow)
@@ -211,7 +212,7 @@ class UserRepositoryImplTest {
     every { domainToBody(user) } returns USER_BODY
     every { responseToDomain(userResponse) } returns user.validNel()
 
-    val result = repo.add(user)
+    val result = either { repo.add(user) }
 
     assertTrue(result.isRight())
     assertNotNull(result.orNull())
@@ -228,7 +229,7 @@ class UserRepositoryImplTest {
     every { domainToBody(user) } returns USER_BODY
     every { errorMapper(ofType()) } returns UserError.NetworkError
 
-    val result = repo.add(user)
+    val result = either { repo.add(user) }
 
     assertTrue(result.isLeft())
     assertEquals(UserError.NetworkError, result.leftOrThrow)
@@ -244,7 +245,7 @@ class UserRepositoryImplTest {
     coEvery { userApiService.search(q) } returns USER_RESPONSES
     every { responseToDomain(any()) } returnsMany VALID_NEL_USERS
 
-    val result = repo.search(q)
+    val result = either { repo.search(q) }
 
     assertTrue(result.isRight())
     assertNotNull(result.orNull())
@@ -264,7 +265,7 @@ class UserRepositoryImplTest {
     coEvery { userApiService.search(q) } throws IOException()
     every { errorMapper(ofType()) } returns UserError.NetworkError
 
-    val result = repo.search(q)
+    val result = either { repo.search(q) }
 
     assertTrue(result.isLeft())
     assertEquals(UserError.NetworkError, result.leftOrThrow)
@@ -337,8 +338,10 @@ class UserRepositoryImplTest {
       val job = launch(start = CoroutineStart.UNDISPATCHED) {
         repo.getUsers().toList(events)
       }
-      repo.add(user)
-      repo.remove(user)
+      either {
+        repo.add(user)
+        repo.remove(user)
+      }.getOrThrow
       delay(120_000)
       job.cancel()
 
diff --git a/domain/src/main/java/com/hoc/flowmvi/domain/repository/UserRepository.kt b/domain/src/main/java/com/hoc/flowmvi/domain/repository/UserRepository.kt
index acb9843e..e60bc747 100644
--- a/domain/src/main/java/com/hoc/flowmvi/domain/repository/UserRepository.kt
+++ b/domain/src/main/java/com/hoc/flowmvi/domain/repository/UserRepository.kt
@@ -1,6 +1,7 @@
 package com.hoc.flowmvi.domain.repository
 
 import arrow.core.Either
+import arrow.core.continuations.EffectScope
 import com.hoc.flowmvi.domain.model.User
 import com.hoc.flowmvi.domain.model.UserError
 import kotlinx.coroutines.flow.Flow
@@ -8,11 +9,15 @@ import kotlinx.coroutines.flow.Flow
 interface UserRepository {
   fun getUsers(): Flow>>
 
-  suspend fun refresh(): Either
+  context(EffectScope)
+  suspend fun refresh()
 
-  suspend fun remove(user: User): Either
+  context(EffectScope)
+  suspend fun remove(user: User)
 
-  suspend fun add(user: User): Either
+  context(EffectScope)
+  suspend fun add(user: User)
 
-  suspend fun search(query: String): Either>
+  context(EffectScope)
+  suspend fun search(query: String): List
 }
diff --git a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/AddUserUseCase.kt b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/AddUserUseCase.kt
index 59eeb0e6..851b5fda 100644
--- a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/AddUserUseCase.kt
+++ b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/AddUserUseCase.kt
@@ -1,10 +1,11 @@
 package com.hoc.flowmvi.domain.usecase
 
-import arrow.core.Either
+import arrow.core.continuations.EffectScope
 import com.hoc.flowmvi.domain.model.User
 import com.hoc.flowmvi.domain.model.UserError
 import com.hoc.flowmvi.domain.repository.UserRepository
 
 class AddUserUseCase(private val userRepository: UserRepository) {
-  suspend operator fun invoke(user: User): Either = userRepository.add(user)
+  context(EffectScope)
+  suspend operator fun invoke(user: User): Unit = userRepository.add(user)
 }
diff --git a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RefreshGetUsersUseCase.kt b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RefreshGetUsersUseCase.kt
index ae6e17a5..70f42e86 100644
--- a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RefreshGetUsersUseCase.kt
+++ b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RefreshGetUsersUseCase.kt
@@ -1,9 +1,10 @@
 package com.hoc.flowmvi.domain.usecase
 
-import arrow.core.Either
+import arrow.core.continuations.EffectScope
 import com.hoc.flowmvi.domain.model.UserError
 import com.hoc.flowmvi.domain.repository.UserRepository
 
 class RefreshGetUsersUseCase(private val userRepository: UserRepository) {
-  suspend operator fun invoke(): Either = userRepository.refresh()
+  context(EffectScope)
+  suspend operator fun invoke(): Unit = userRepository.refresh()
 }
diff --git a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RemoveUserUseCase.kt b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RemoveUserUseCase.kt
index fce13af0..8a2c2917 100644
--- a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RemoveUserUseCase.kt
+++ b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/RemoveUserUseCase.kt
@@ -1,10 +1,11 @@
 package com.hoc.flowmvi.domain.usecase
 
-import arrow.core.Either
+import arrow.core.continuations.EffectScope
 import com.hoc.flowmvi.domain.model.User
 import com.hoc.flowmvi.domain.model.UserError
 import com.hoc.flowmvi.domain.repository.UserRepository
 
 class RemoveUserUseCase(private val userRepository: UserRepository) {
-  suspend operator fun invoke(user: User): Either = userRepository.remove(user)
+  context(EffectScope)
+  suspend operator fun invoke(user: User): Unit = userRepository.remove(user)
 }
diff --git a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/SearchUsersUseCase.kt b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/SearchUsersUseCase.kt
index fd6713c0..b1c22afd 100644
--- a/domain/src/main/java/com/hoc/flowmvi/domain/usecase/SearchUsersUseCase.kt
+++ b/domain/src/main/java/com/hoc/flowmvi/domain/usecase/SearchUsersUseCase.kt
@@ -1,11 +1,12 @@
 package com.hoc.flowmvi.domain.usecase
 
-import arrow.core.Either
+import arrow.core.continuations.EffectScope
 import com.hoc.flowmvi.domain.model.User
 import com.hoc.flowmvi.domain.model.UserError
 import com.hoc.flowmvi.domain.repository.UserRepository
 
 class SearchUsersUseCase(private val userRepository: UserRepository) {
-  suspend operator fun invoke(query: String): Either> =
+  context(EffectScope)
+  suspend operator fun invoke(query: String): List =
     userRepository.search(query)
 }
diff --git a/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt b/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt
index a0bbb45e..885dc99c 100644
--- a/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt
+++ b/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt
@@ -1,5 +1,6 @@
 package com.hoc.flowmvi.domain
 
+import arrow.core.continuations.either
 import arrow.core.left
 import arrow.core.right
 import com.hoc.flowmvi.domain.model.User
@@ -11,12 +12,16 @@ import com.hoc.flowmvi.domain.usecase.RefreshGetUsersUseCase
 import com.hoc.flowmvi.domain.usecase.RemoveUserUseCase
 import com.hoc.flowmvi.domain.usecase.SearchUsersUseCase
 import com.hoc.flowmvi.test_utils.TestCoroutineDispatcherRule
+import com.hoc.flowmvi.test_utils.justShift
 import com.hoc.flowmvi.test_utils.valueOrThrow
+import com.hoc.flowmvi.test_utils.withAnyEffectScope
+import io.mockk.Runs
 import io.mockk.clearAllMocks
 import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.confirmVerified
 import io.mockk.every
+import io.mockk.just
 import io.mockk.mockk
 import io.mockk.verify
 import kotlin.test.AfterTest
@@ -65,7 +70,8 @@ class UseCaseTest {
   private lateinit var addUserUseCase: AddUserUseCase
   private lateinit var searchUsersUseCase: SearchUsersUseCase
 
-  private val errorLeft = UserError.NetworkError.left()
+  private val networkError = UserError.NetworkError
+  private val errorLeft = networkError.left()
 
   @BeforeTest
   fun setup() {
@@ -107,83 +113,83 @@ class UseCaseTest {
 
   @Test
   fun test_refreshUseCase_whenSuccess_returnsUnit() = runTest {
-    coEvery { userRepository.refresh() } returns Unit.right()
+    coEvery { withAnyEffectScope { userRepository.refresh() } } just Runs
 
-    val result = refreshUseCase()
+    val result = either { refreshUseCase() }
 
-    coVerify { userRepository.refresh() }
+    coVerify { withAnyEffectScope { userRepository.refresh() } }
     assertEquals(Unit.right(), result)
   }
 
   @Test
   fun test_refreshUseCase_whenError_throwsError() = runTest {
-    coEvery { userRepository.refresh() } returns errorLeft
+    coEvery { withAnyEffectScope { userRepository.refresh() } } justShift networkError
 
-    val result = refreshUseCase()
+    val result = either { refreshUseCase() }
 
-    coVerify { userRepository.refresh() }
+    coVerify { withAnyEffectScope { userRepository.refresh() } }
     assertEquals(errorLeft, result)
   }
 
   @Test
   fun test_removeUserUseCase_whenSuccess_returnsUnit() = runTest {
-    coEvery { userRepository.remove(any()) } returns Unit.right()
+    coEvery { withAnyEffectScope { userRepository.remove(any()) } } just Runs
 
-    val result = removeUserUseCase(USERS[0])
+    val result = either { removeUserUseCase(USERS[0]) }
 
-    coVerify { userRepository.remove(USERS[0]) }
+    coVerify { withAnyEffectScope { userRepository.remove(USERS[0]) } }
     assertEquals(Unit.right(), result)
   }
 
   @Test
   fun test_removeUserUseCase_whenError_throwsError() = runTest {
-    coEvery { userRepository.remove(any()) } returns errorLeft
+    coEvery { withAnyEffectScope { userRepository.remove(any()) } } justShift networkError
 
-    val result = removeUserUseCase(USERS[0])
+    val result = either { removeUserUseCase(USERS[0]) }
 
-    coVerify { userRepository.remove(USERS[0]) }
+    coVerify { withAnyEffectScope { userRepository.remove(USERS[0]) } }
     assertEquals(errorLeft, result)
   }
 
   @Test
   fun test_addUserUseCase_whenSuccess_returnsUnit() = runTest {
-    coEvery { userRepository.add(any()) } returns Unit.right()
+    coEvery { withAnyEffectScope { userRepository.add(any()) } } just Runs
 
-    val result = addUserUseCase(USERS[0])
+    val result = either { addUserUseCase(USERS[0]) }
 
-    coVerify { userRepository.add(USERS[0]) }
+    coVerify { withAnyEffectScope { userRepository.add(USERS[0]) } }
     assertEquals(Unit.right(), result)
   }
 
   @Test
   fun test_addUserUseCase_whenError_throwsError() = runTest {
-    coEvery { userRepository.add(any()) } returns errorLeft
+    coEvery { withAnyEffectScope { userRepository.add(any()) } } justShift networkError
 
-    val result = addUserUseCase(USERS[0])
+    val result = either { addUserUseCase(USERS[0]) }
 
-    coVerify { userRepository.add(USERS[0]) }
+    coVerify { withAnyEffectScope { userRepository.add(USERS[0]) } }
     assertEquals(errorLeft, result)
   }
 
   @Test
   fun test_searchUsersUseCase_whenSuccess_returnsUsers() = runTest {
-    coEvery { userRepository.search(any()) } returns USERS.right()
+    coEvery { withAnyEffectScope { userRepository.search(any()) } } returns USERS
 
     val query = "hoc081098"
-    val result = searchUsersUseCase(query)
+    val result = either { searchUsersUseCase(query) }
 
-    coVerify { userRepository.search(query) }
+    coVerify { withAnyEffectScope { userRepository.search(query) } }
     assertEquals(USERS.right(), result)
   }
 
   @Test
   fun test_searchUsersUseCase_whenError_throwsError() = runTest {
-    coEvery { userRepository.search(any()) } returns errorLeft
+    coEvery { withAnyEffectScope { userRepository.search(any()) } } justShift networkError
 
     val query = "hoc081098"
-    val result = searchUsersUseCase(query)
+    val result = either { searchUsersUseCase(query) }
 
-    coVerify { userRepository.search(query) }
+    coVerify { withAnyEffectScope { userRepository.search(query) } }
     assertEquals(errorLeft, result)
   }
 }
diff --git a/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddVM.kt b/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddVM.kt
index 04c43e36..ebfc61db 100644
--- a/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddVM.kt
+++ b/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddVM.kt
@@ -2,13 +2,13 @@ package com.hoc.flowmvi.ui.add
 
 import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.viewModelScope
+import arrow.core.continuations.effect
 import arrow.core.orNull
 import com.hoc.flowmvi.core.dispatchers.AppCoroutineDispatchers
 import com.hoc.flowmvi.domain.model.User
 import com.hoc.flowmvi.domain.usecase.AddUserUseCase
 import com.hoc.flowmvi.mvi_base.AbstractMviViewModel
 import com.hoc081098.flowext.flatMapFirst
-import com.hoc081098.flowext.flowFromSuspend
 import com.hoc081098.flowext.mapTo
 import com.hoc081098.flowext.startWith
 import com.hoc081098.flowext.withLatestFrom
@@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.merge
@@ -112,11 +113,15 @@ class AddVM(
       .withLatestFrom(userFormFlow) { _, userForm -> userForm }
       .mapNotNull { it?.orNull() }
       .flatMapFirst { user ->
-        flowFromSuspend { addUser(user) }
+        flowOf(
+          effect {
+            addUser(user)
+          }
+        )
           .map { result ->
             result.fold(
-              ifLeft = { PartialStateChange.AddUser.AddUserFailure(user, it) },
-              ifRight = { PartialStateChange.AddUser.AddUserSuccess(user) }
+              recover = { PartialStateChange.AddUser.AddUserFailure(user, it) },
+              transform = { PartialStateChange.AddUser.AddUserSuccess(user) }
             )
           }
           .startWith(PartialStateChange.AddUser.Loading)
diff --git a/feature-add/src/test/java/com/hoc/flowmvi/ui/add/AddVMTest.kt b/feature-add/src/test/java/com/hoc/flowmvi/ui/add/AddVMTest.kt
index 0f03a327..cecc0ab2 100644
--- a/feature-add/src/test/java/com/hoc/flowmvi/ui/add/AddVMTest.kt
+++ b/feature-add/src/test/java/com/hoc/flowmvi/ui/add/AddVMTest.kt
@@ -1,8 +1,6 @@
 package com.hoc.flowmvi.ui.add
 
 import androidx.lifecycle.SavedStateHandle
-import arrow.core.left
-import arrow.core.right
 import com.hoc.flowmvi.domain.model.User
 import com.hoc.flowmvi.domain.model.UserError
 import com.hoc.flowmvi.domain.model.UserValidationError
@@ -10,10 +8,12 @@ import com.hoc.flowmvi.domain.model.UserValidationError.TOO_SHORT_FIRST_NAME
 import com.hoc.flowmvi.domain.model.UserValidationError.TOO_SHORT_LAST_NAME
 import com.hoc.flowmvi.domain.usecase.AddUserUseCase
 import com.hoc.flowmvi.mvi_testing.BaseMviViewModelTest
+import com.hoc.flowmvi.mvi_testing.justShiftWithDelay
 import com.hoc.flowmvi.mvi_testing.mapRight
 import com.hoc.flowmvi.mvi_testing.returnsWithDelay
 import com.hoc.flowmvi.test_utils.TestAppCoroutineDispatchers
 import com.hoc.flowmvi.test_utils.valueOrThrow
+import com.hoc.flowmvi.test_utils.withAnyEffectScope
 import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.confirmVerified
@@ -279,7 +279,7 @@ class AddVMTest : BaseMviViewModelTest
           result.fold(
-            ifLeft = { PartialChange.Refresh.Failure(it) },
-            ifRight = { PartialChange.Refresh.Success }
+            recover = { PartialChange.Refresh.Failure(it) },
+            transform = { PartialChange.Refresh.Success }
           )
         }
         .startWith(PartialChange.Refresh.Loading)
@@ -123,15 +121,16 @@ class MainVM(
           .log("Intent")
           .map { it.user }
           .flatMapMerge { userItem ->
-            flowFromSuspend {
-              userItem
-                .toDomain()
-                .flatMap { removeUser(it) }
-            }
+            flowOf(
+              effect {
+                val user = userItem.toDomain().bind()
+                removeUser(user)
+              }
+            )
               .map { result ->
                 result.fold(
-                  ifLeft = { PartialChange.RemoveUser.Failure(userItem, it) },
-                  ifRight = { PartialChange.RemoveUser.Success(userItem) },
+                  recover = { PartialChange.RemoveUser.Failure(userItem, it) },
+                  transform = { PartialChange.RemoveUser.Success(userItem) },
                 )
               }
           }
diff --git a/feature-main/src/test/java/com/hoc/flowmvi/ui/main/MainVMTest.kt b/feature-main/src/test/java/com/hoc/flowmvi/ui/main/MainVMTest.kt
index c0a295e8..11c2ef7c 100644
--- a/feature-main/src/test/java/com/hoc/flowmvi/ui/main/MainVMTest.kt
+++ b/feature-main/src/test/java/com/hoc/flowmvi/ui/main/MainVMTest.kt
@@ -9,9 +9,12 @@ import com.hoc.flowmvi.domain.usecase.RefreshGetUsersUseCase
 import com.hoc.flowmvi.domain.usecase.RemoveUserUseCase
 import com.hoc.flowmvi.mvi_testing.BaseMviViewModelTest
 import com.hoc.flowmvi.mvi_testing.delayEach
+import com.hoc.flowmvi.mvi_testing.justShiftWithDelay
 import com.hoc.flowmvi.mvi_testing.mapRight
 import com.hoc.flowmvi.mvi_testing.returnsWithDelay
 import com.hoc.flowmvi.test_utils.TestAppCoroutineDispatchers
+import com.hoc.flowmvi.test_utils.justShift
+import com.hoc.flowmvi.test_utils.withAnyEffectScope
 import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.coVerifySequence
@@ -120,7 +123,7 @@ class MainVMTest : BaseMviViewModelTest
             either.map { users ->
-              users.filter { it.id != firstArg().id }
+              users.filter { it.id != secondArg().id }
             }
           }
           Unit.right()
@@ -383,8 +386,8 @@ class MainVMTest : BaseMviViewModelTest.toPartialStateChangesFlow(): Flow {
     val executeSearch: suspend (String) -> Flow = { query: String ->
-      flowFromSuspend { searchUsersUseCase(query) }
+      flowOf(effect { searchUsersUseCase(query) })
         .map { result ->
           result.fold(
-            ifLeft = { PartialStateChange.Failure(it, query) },
-            ifRight = {
+            recover = { PartialStateChange.Failure(it, query) },
+            transform = {
               PartialStateChange.Success(
                 it.map(UserItem::from),
                 query
diff --git a/feature-search/src/test/java/com/hoc/flowmvi/ui/search/SearchVMTest.kt b/feature-search/src/test/java/com/hoc/flowmvi/ui/search/SearchVMTest.kt
index 9e29b66b..51aeb77a 100644
--- a/feature-search/src/test/java/com/hoc/flowmvi/ui/search/SearchVMTest.kt
+++ b/feature-search/src/test/java/com/hoc/flowmvi/ui/search/SearchVMTest.kt
@@ -1,15 +1,15 @@
 package com.hoc.flowmvi.ui.search
 
 import androidx.lifecycle.SavedStateHandle
-import arrow.core.left
-import arrow.core.right
 import com.hoc.flowmvi.domain.model.UserError
 import com.hoc.flowmvi.domain.usecase.SearchUsersUseCase
 import com.hoc.flowmvi.mvi_testing.BaseMviViewModelTest
+import com.hoc.flowmvi.mvi_testing.justShiftWithDelay
 import com.hoc.flowmvi.mvi_testing.mapRight
-import com.hoc.flowmvi.mvi_testing.returnsManyWithDelay
 import com.hoc.flowmvi.mvi_testing.returnsWithDelay
 import com.hoc.flowmvi.test_utils.TestAppCoroutineDispatchers
+import com.hoc.flowmvi.test_utils.shift
+import com.hoc.flowmvi.test_utils.withAnyEffectScope
 import com.hoc.flowmvi.ui.search.SearchVM.Companion.SEARCH_DEBOUNCE_DURATION
 import com.hoc081098.flowext.concatWith
 import com.hoc081098.flowext.timer
@@ -60,7 +60,7 @@ class SearchVMTest : BaseMviViewModelTest cancelled by (2)
-      USERS.right()
+      USERS
     }
-    coEvery { searchUsersUseCase(query2) } returns USERS.right()
+    coEvery { withAnyEffectScope { searchUsersUseCase(query2) } } returns USERS
 
     runVMTest(
       vmProducer = { vm },
@@ -305,8 +305,8 @@ class SearchVMTest : BaseMviViewModelTest shift(networkError)
+        1 -> {
+          delay(1)
+          USERS
+        }
+        else -> error("Should not reach here!")
+      }
+    }
 
     runVMTest(
       vmProducer = { vm },
@@ -449,7 +456,7 @@ class SearchVMTest : BaseMviViewModelTest networkError.left()
+        0 -> shift(networkError)
         1 -> {
           repeat(3) { timeout() } // (1) very long ... -> cancelled by (2)
-          USERS.right()
+          USERS
         }
         else -> error("Should not reach here!")
       }
     }
-    coEvery { searchUsersUseCase(query2) } returns USERS.right()
+    coEvery { withAnyEffectScope { searchUsersUseCase(query2) } } returns USERS
 
     runVMTest(
       vmProducer = { vm },
@@ -562,9 +569,9 @@ class SearchVMTest : BaseMviViewModelTest MockKStubScope.returnsManyWithDelay(values: List) {
   }
 }
 
+/**
+ * Workaround for [Kotlin/kotlinx.coroutines/issues/3120](https://github.com/Kotlin/kotlinx.coroutines/issues/3120).
+ * TODO(coroutines): https://github.com/Kotlin/kotlinx.coroutines/issues/3120
+ */
+inline infix fun  MockKStubScope<*, *>.justShiftWithDelay(r: R) {
+  coAnswers {
+    delay(1)
+    shift(r)
+  }
+}
+
 @ExperimentalTime
 @ExperimentalCoroutinesApi
 abstract class BaseMviViewModelTest<
diff --git a/test-utils/src/main/java/com/hoc/flowmvi/test_utils/utils.kt b/test-utils/src/main/java/com/hoc/flowmvi/test_utils/utils.kt
index 1f09c584..1ee512bc 100644
--- a/test-utils/src/main/java/com/hoc/flowmvi/test_utils/utils.kt
+++ b/test-utils/src/main/java/com/hoc/flowmvi/test_utils/utils.kt
@@ -2,9 +2,14 @@ package com.hoc.flowmvi.test_utils
 
 import arrow.core.Either
 import arrow.core.Validated
+import arrow.core.continuations.EffectScope
 import arrow.core.getOrHandle
 import arrow.core.identity
 import arrow.core.valueOr
+import com.hoc.flowmvi.core.unit
+import io.mockk.MockKAnswerScope
+import io.mockk.MockKMatcherScope
+import io.mockk.MockKStubScope
 
 inline val  Validated.valueOrThrow: A
   get() = valueOr(this::throws)
@@ -22,3 +27,15 @@ inline val  Either.getOrThrow: R
 internal fun  Any.throws(it: E): Nothing =
   if (it is Throwable) throw it
   else error("$this - $it - Should not reach here!")
+
+context(MockKMatcherScope)
+inline fun  withAnyEffectScope(block: EffectScope.() -> RR): RR =
+  with(any>()) {
+    block()
+  }
+
+inline infix fun  MockKStubScope<*, *>.justShift(r: R): Unit =
+  coAnswers { shift(r) }.unit
+
+suspend inline infix fun  MockKAnswerScope<*, *>.shift(r: R): Nothing =
+  arg>(0).shift(r)