Different order using fireAndForget vs run, inside concatenate

I'm currently using Composable Architecture 0.57.0. I'm gradually going to migrate to 1.0.0. I started working on some warnings. One that is causing a different behavior than the one I expect is replacing 'fireAndForget' with 'Effect.run, when I use them inside concatenate.

If I have a concatenate with many action calls and some fireAndForget, the functions in the fireAndForget would be executed before the ones that were indirectly called by the actions. When I replaced the code for .run I noticed that the order changed. In fireAndForget the functions ran by default on the main thread, which is not the case within .run, I tried running everything using the MainActor but the order is still different.

I could move that code out from concatenate, and just run it directly in the case action block , but I don't want to have effects executing without control, since it would be harder to test, although if fixes my order problem.

Here is an example. I need to keep the same order I get with fireAndForget but using .run.

import Testing
import ComposableArchitecture

var numbersFire: [Int] = []
var numbersRun: [Int] = []

struct OrderTestingTGATests {
    @Test func exampleFire() async throws {
        struct OrderFeature: Reducer {
            struct State: Equatable {}
            enum Action: String, CaseIterable {
                case concat, add3, add4, add5, add6
            }
            var body: some ReducerOf<Self> {
                Reduce { state, action in
                    switch action {
                    case .add3:
                        return .fireAndForget { numbersFire.append(3) }
                    case .add4:
                        return .fireAndForget { numbersFire.append(4) }
                    case .add5:
                        return .fireAndForget { numbersFire.append(5) }
                    case .add6:
                        return .fireAndForget { numbersFire.append(6) }
                    case .concat:
                        return .concatenate(
                            .send(.add3),
                            .send(.add4),
                            .fireAndForget { numbersFire.append(1) },
                            .fireAndForget { numbersFire.append(2) },
                            .send(.add5),
                            .send(.add6)
                        )
                    }
                }
            }
        }
        let store = Store(initialState: OrderFeature.State(), reducer: OrderFeature())
        store.send(.concat)
        // Test passes
        #expect(numbersFire == [1, 2, 3, 4, 5, 6])
    }
    
    @Test func exampleRun() async throws {
        struct OrderFeature: Reducer {
            struct State: Equatable {}
            enum Action: String, CaseIterable {
                case concat, add3, add4, add5, add6
            }
            var body: some ReducerOf<Self> {
                Reduce { state, action in
                    switch action {
                    case .add3:
                        return .run { _ in await MainActor.run { numbersRun.append(3) } }
                    case .add4:
                        return .run { _ in await MainActor.run { numbersRun.append(4) } }
                    case .add5:
                        return .run { _ in await MainActor.run { numbersRun.append(5) } }
                    case .add6:
                        return .run { _ in await MainActor.run { numbersRun.append(6) } }
                    case .concat:
                        return .concatenate(
                            .send(.add3),
                            .send(.add4),
                            .run { _ in await MainActor.run { numbersRun.append(1) } },
                            .run { _ in await MainActor.run { numbersRun.append(2) } },
                            .send(.add5),
                            .send(.add6)
                        )
                    }
                }
            }
        }
        let store = Store(initialState: OrderFeature.State(), reducer: OrderFeature())
        store.send(.concat)
        try await Task.sleep(for: .milliseconds(100))
        // Expectation failed: (numbersRun → [1, 3, 4, 2, 5, 6]) == [1, 2, 3, 4, 5, 6]
        #expect(numbersRun == [1, 2, 3, 4, 5, 6])
    }
}