I went in a bit sceptical, but the proposal is well-written and has convinced me this is a fruitful direction.

Explicitly using the global concurrent executor
If a library really wants to ensure that hops to the global concurrent executor are made by e.g. such task group, they should use group.addTask(on: nil) to override the inherited task executor preference.
Using nil as a magic constant in this way seems a bit, well, magical. What if instead there were some suitable static member on TaskExecutor, e.g. globalConcurrentExecutor?
group.addTask(on: .globalConcurrentExecutor)
More verbose but also much clearer and more intuitive to the reader.
Serial executors (and parallels to GCD)
Since serial executors are executors, they can also be used with this API. However since serial executors are predominantly used by actors, in tandem with actor isolation — there is a better way to run tasks on a specific actor, and therefore its serial executor.
I was already thinking, up to this point in the proposal, that this was sounding a lot like reimplementing GCD in Swift Concurrency. The above point kinda emphasises it.
I'm not sure if that's good or bad, but it seems worth addressing that more directly in the proposal. e.g. to what degree is that or isn't that the objective, what are the remaining distinctions (if any) after this proposal is implemented, etc.
And spin-off questions like can / how can you use a GCD queue as a TaskExecutor (or conversely use GCD APIs to enqueue work on TaskExecutors)?
Main task executor
For example, over-hanging on the MainActor's executor is one fo the main reasons earlier Swift versions moved to make nonisolated asynchronous functions always hop off their calling execution context; and this proposal brings back this behavior for specific executors.
Ironically, this makes me ponder if there should be a ".mainActor" TaskExecutor so that you can dynamically (or "manually") tie a task to the main thread (as opposed to using static declarations of @MainActor). And I see that this is in fact part of the proposal (albeit buried at the end).
I suspect that's a better way to put things on the main thread than having lots of MainActor.run { … } and similar constructs scattered about. As the proposal notes, "hacks" like { @MainActor in … } have unnecessary performance costs too.
Although this is covered under "Future directions", it seems to be saying that this will already work with this proposal? If so, maybe it should be moved out of "Future directions", and also I suggest a compiler diagnostic be added [as part of this proposal] for that { @MainActor in … } pattern, with a FixIt to use the more efficient form.
SwiftUI
I know it's outside the purview of SEPs, as a proprietary Apple framework, but I think it's instructive to consider whether the SwiftUI task view modifier should have a task(on: TaskExecutor) variant added?
Generally the default - of tying such tasks to the main thread - is appropriate, but I have found myself occasionally wanting to put things on other threads, and having to put await Task.detached { … } around such things is fine but seems inelegant in light of this proposal.
Is there any reason SwiftUI (and similar frameworks) would not want to follow this on: TaskExecutor pattern?
Blocking inside Tasks
…IO systems which willingly perform blocking operations and need to perform them off the global concurrency pool.
This seems like burying the lede. If I understand it correctly, the ability to use a custom TaskExecutor means you can finally oversubscribe CPU cores (by providing your own thread pools with arbitrary or even unbounded numbers of threads)? So you can (to a degree) safely use blocking code inside Tasks?
I'm a big fan of this - I don't buy into the "the whole world should be async so deadlock from blocking becomes moot" plan, at least from a will-we-ever-actually-get-there perspective - but it seems expressly at odds with how many Swift team members feel Structured Concurrency should work [as a matter of principle].
Progressive disclosure
I like the emphasis on progressive disclosure, and I think it's the right counter-balance to having "expert" controls.
A lot of what's being enabled in this proposal is [in essence] the ability to "regress" back to manually managing execution on specific threads (which may, out of scope of this proposal, even be tied to specific cores etc). Very powerful, but it must be underscored that most Swift users should not need to be aware of nor utilising this, most of the time.
Which seems to be exactly the proposal's attitude. I'm just emphasising its importance.
Is it fair to say that the mental flowchart is intended to be something like:
- Is my program slow? If no, break.
- Separate things into detached Tasks (appropriately). If no longer slow, break.
- Fine-tune thread (and core) assignment via manual
TaskExecutor control (appropriately).
AsyncSequence
The AsyncSequence example is a particularly important one. I've been bitten by exactly that performance pitfall quite a few times, including in seemingly trivial code that's just async iterating over something (e.g. lines of a file) from the main thread. It'll be good to at least have a way to fix that, albeit manually.
I still feel like AsyncSequence should just work fast by default, though. This feels more like a bandaid than a cure.
Thread pools by any other name…?
The proposal points out more than once that conceptually TaskExecutor is essentially delineating a thread pool. I can see that there's some symmetry, of sorts, with a name like TaskExecutor, but then given the apparent need to frequently explain what that really means… should it just be called ThreadPool?
Conceptually I see some merit in distinguishing between isolation domains and thread pools, because they're potentially orthogonal. Actors have isolation requirements which might be implemented by executing them only a specific thread, but not necessarily. Etc.
Parse error
Thanks to the improvements to treating @SomeGlobalActor isolation proposed in SE-NNNN: Improved control over closure actor isolation we would be able to that a Task may prefer to run on a specific global actor’s executor, and shall be isolated to that actor.
I'm not sure what the above is trying to say…?