Hi, Disclosure: I gave feedback on this work at an earlier stage via direct email, so some of this echoes points I raised before. Overall, these are solid features, and the papers are well written. Extending coroutines to close the gaps C++20 left around affinity and cancellation is genuinely important work, and I am glad to see it. The motivation chapters are among the strongest parts, "Complicated Success" in particular, and the compound-result argument is compelling. **1. Is it worth standardizing?** Yes, the underlying problem is real and worth solving. Had these papers been submitted three years ago, they would likely have been accepted with no major issues. The difficulty is that we now have `std::execution` in C++26, and proposing a second model for structured asynchronism is a much harder sell today. The papers motivate *why* a second model is needed and why `std::execution` struggles with byte-movement requirements, and the `await_sender`/`as_sender` interop helps, but I do not think it is yet enough to carry LEWG. My biggest concern here is that the proposal duplicates a large amount of synchronization and pipeline machinery, and that will be treated as a serious issue in LEWG. The domain argument justifies a second *execution model*. It does not obviously justify a second *set of algorithms*. Re-deriving `when_all`, `when_any`, `on_timeout`, and the rest for two independent frameworks is the kind of thing the room will resist strongly. The paper should demonstrate whether the existing `std::execution` algorithms can be customized to run with no overhead over a custom `io_scheduler`, and if they cannot, state exactly why. Without that analysis the fork will not survive LEWG. The same applies to the types: - A second `task` is likely a no-go, as C++26 already took `std::execution::task`. The paper should show whether the accepted C++26 `task` can be made to satisfy the IoAwaitable protocol with no overhead (for example via promise/allocator customization), and justify a separate type only if it cannot. If a separate type does turn out to be unavoidable, please name it `io_task` at minimum. - `std::execution` already has `system_context`/`parallel_scheduler`, yet P4100 adds both `thread_pool` and `system_context`. The paper should discuss whether one generic type can serve both `std::execution` and io_awaitables with no overhead, and if it cannot, why a separate type is required. So: worth standardizing in principle, but the duplication needs to be reduced or very strongly justified before it advances. **2. Did the use of AI affect your perception?** Yes, somewhat. A few passages read as marketing copy in a register that is not WG21 paper style, and others in this thread have pointed at the same lines. More importantly, the paper is simultaneously long and too brief. A lot of the design needed to understand what is going on is missing, forcing the reader to reconstruct it across multiple papers. That is frustrating, and a proposal like this should be self-contained. My impression is that the material was organized in a way that is easy for an AI to parse and reason over all at once, but a human reader does not have that luxury. (Ruben and Matt make essentially the same observation.) **3. What is the technical quality?** The concepts are mostly sound and clearly stated as real C++. My chief worry is the automatic propagation of the frame allocator in "Out of band" mode, which the paper seems to prefer. Relying on thread-local (or static) storage for the allocator is unlikely to be welcomed in LEWG, and I would expect heavy discussion on it. The paper currently seems to sidestep the issue rather than confront it. Please add an explicit, well-argued rationale rather than leaving the mechanism implicit, covering correctness under arbitrary resumption points, who is responsible for save/restore, and what happens when it goes wrong. (Gennaro raised the same save/restore correctness concern, and it deserves a direct answer in the paper.) A few more specific technical questions: - Is `nullptr` a valid value for the `env` argument of `await_suspend(h, env)`? The "borrowed pointer" rationale is fair, but if `env` is always non-null, a reference expresses exactly that (borrowed, never null) and keeps the compile-time boundary check. If null *is* meaningful, the protocol should say so, because the P4251 examples dereference `env->executor` unconditionally. - `Executor` concept: - (a) `std::equality_comparable<E>` could replace `{ ce == ce2 } noexcept -> std::convertible_to<bool>`, though you would need to add the `noexcept` back, since Asio-style executors want it. - (b) `dispatch` requiring `same_as<std::coroutine_handle<>>` means a concrete executor returning a specific handle type will not satisfy it. I would use `convertible_to<std::coroutine_handle<>>` or an exposition-only `specialization-of<std::coroutine_handle>`, unless the erased handle is a deliberate symmetric-transfer contract, in which case please say so. - (c) `on_work_started()`/`on_work_finished()` are mandatory in the concept. Should they be? - `executor_ref(E const&)` is non-explicit and stores `&e`, which is a dangling hazard when constructed from a temporary. An `explicit` constructor plus a deleted rvalue overload would help. - `execution_context::service` is a polymorphic base but does not delete copy/move. It should be non-copyable (and likely non-movable) to avoid slicing. - `IoRunnable` requires `typename T::promise_type` directly, which is too constraining. It should go through `std::coroutine_traits<T>::promise_type`, the language's actual customization point. **4. What is missing?** Beyond the consolidated normative section already mentioned, here are the items I looked for but did not find. *Naming.* `io_stream` collides conceptually with `istream`/`ostream` and `<iostream>`. A `std::io` namespace would resolve several of these at once. You already show it in P4100 §13 (`std::io::task`, `std::io::any_stream`), so I would commit to it consistently and drop the redundant `io_` prefixes. (Mohammad's "IO prefix is unnecessarily restrictive" points the same way.) Also: `run_async()` spawns a detached coroutine, which is potentially harmful. If multiple detached launches before `ctx.run()` are intended, `schedule()` or `spawn()` reads better, since `run_async()` does not actually run anything. We suspend at the initial suspend, correct? And `run()` reads like a blocking call, but it is the in-coroutine operation that switches executor/stop-token/allocator and is always `co_await`-ed. That is `on`/`continue_on`/`with_context`, with `with_context()` the best fit. Note that P4100 §8.2 calls `run()` "blocks until the task completes," contradicting the other papers. One of those needs fixing. The papers also use "executors" (pre-P2300) where `std::execution` has "schedulers." Two vocabularies for adjacent ideas are a teachability cost. If the Asio-style `Executor` (dispatch/post/context) is kept deliberately, please own that cost explicitly. If teachability is not discussed anywhere, it should be. See P3045R9 for examples. *Adapters/interop gaps.* The explicit `as_sender`/`await_sender` is great. An implicit path would be nice too. Could `io_task`'s `await_transform` recognize a sender and apply `await_sender` under the hood, so the bridge is as invisible as today's coroutine/sender interop? And could `std::execution` abstractions be taught to treat io_awaitables like regular coroutines, running `as_sender`/`await_sender` internally? Also missing: how to await an io_awaitable from a *regular* (non-IO) coroutine. Not every coroutine will be an `io_task`, yet people may still need to wait on an IO event asynchronously. Is `run()` meant to cover that? *Cancellation.* Should `run_async` take a cancellation handler? It currently takes `(executor, value, error)` in fixed order with no stopped/cancel channel. Could the handler order be deduced from the concepts the callables satisfy, so a caller who only cares about cancellation or error need not pass a dummy value continuation? *Shape of `IoRunnable`.* It exposes `release()`, `handle()`, `exception()`, `set_continuation()`, and `set_environment()` on the public surface even though they are only used inside the launch machinery. Users may call them incorrectly (for example `release()`) in coroutine code. A coroutine-shell approach like my `sync_await`/`synchronized_task` (a shell over a regular `co_await`) hides this at the cost of one allocation: https://github.com/mpusz/mp-coro/blob/main/src/include/mp-coro/sync_await.h. Relatedly, is there a `sync_await`-style blocking entry point returning a value to `main()`, and will it be customized for io_awaitables? *ABI stability.* The papers repeatedly stress that no recompilation of type-erased code is needed. Is that mainly a differentiator from `std::execution`, or a genuinely strong industry requirement? I am asking sincerely. If it is the latter, consolidate the evidence (field report, Boost adopters) in one place and quantify it, and separate "ABI stability is valuable" from "therefore a coroutine-native model is required to get it." *Process / strategy.* This is not about the paper itself, but about how to advance it. I would strongly recommend securing SG4 (Networking) buy-in before LEWG. SG4 understands the networking reality and is best placed to judge where `std::execution` falls short here. Strong SG4 backing makes LEWG progress much easier, whereas going to LEWG first risks a wall that is hard to recover from, even with later SG4 support. (I take my own unit-library papers to SG6 first for the same reason.) I would also expect the relevant chairs to want you and Dietmar Kühl to converge on a common approach before LEWG acts, so doing that early is worthwhile. If you do go to LEWG directly, you may want to request an evening session to build an audience and find supporters ahead of the main-room discussion, and focus it on "Why?" rather than "How?" That sequencing has worked well for me. Thanks for putting this together. I hope the feedback above is useful. Mateusz