I said it in the thread you linked, but the problem with reading the function body isn’t limited to backwards compatibility or stubs. It also means someone can erroneously change the function body without touching the type signature and have the type of the function change.
In line with your second listed option, I think this is the only reasonable option.
for idiomatic async code, nothing changes. for odd cases like a coroutine function that returns an AsyncIterator without being one itself, you’d need:
async def foo() -> Coroutine[Any, Any, AsyncIterator[int]]:
...