I in particular like the output gates, I really like that way of handling transaction durability, where you do all the work, but external side-effects are only triggered when the transaction successfully commits. That, combined with the caching makes it really powerful.
There is a case for input gates, I didn't completely follow at first, and I think it might be worth adding as an example to the article.
What happens if I do:
promise = this.storage.get("counter")
promise2 = fetch("blah")
If you have two requests doing this, then you may end up with races again.
It's akin to your later example with two concurrent storage operations, but in this case there is only one read.