What was the rationale for splitting it up this way?
Was it simply to allow Stream to remain in futures-core while StreamExt was moved into futures-util, to help stabilize futures-core?
Or is there some deeper problem with the way trait default methods work that make ...Ext traits the preferred way to provide utility methods like filter and map in new code?
The commit message doesn't mention an issue or a PR, and I haven't been able to find the discussion around this.
Yes, that's exactly it. The rationale is spelled out in the Futures 0.2 proposal in the futures-rfcs repo:
futures-core will include Future, Stream, IntoFuture traits and their dependencies. This is a central crate that includes only essential items, designed to minimize future breakage.
Combinator methods will be removed from the Future and Stream and moved to the FutureExt and StreamExt traits in the futures-util crate. poll will be the only remaining method on the Future and Stream traits.
This separation allows combinator functions to evolve over time without breaking changes to the public API surface of async libraries.