Martijn, I also noticed that TopGroupsShardResponseProcessor can't deal with multiple ShardRequests (although it looks like it wouldn't be to hard to add this ability). At any rate, your approach of returning a single ShardRequest containing all relevant shards sounds like the right one. I went one step further and refactored TopGroupsShardRequestFactory.java because there was significant code duplication in the class's two primary methods.
In my testing I also discovered a closely related problem. The bug is in the data structure used to map search groups to the shards which contain them. ResponseBuilder.searchGroupToShard assumes that a given search group only resides on one shard. I could not find this assumption documented anywhere, nor can I find a reason such a restriction need be imposed. This structure is populated by SearchGroupShardResponseProcessor. There is a race condition there, wherein the last shard to report a search group will be assumed to be the only shard containing the search group. This data structure is used in TopGroupsShardRequestFactory.createRequestForSpecificShards() to know which shards to query. This means you can get a different set of shards to query depending on shard query order.
I have changed the structure to allow a search group to be present in multiple shards.
Patch to follow.