It may still end up losing to Envoy/BM, but I don't think ChapelEnvoy plays optimally as it does things like this (this is bear in mind from rspeer's simulator, not Geronimoo's):
== EnvoyChapel's turn 3 ==
EnvoyChapel plays Copper.
EnvoyChapel plays Copper.
EnvoyChapel plays Copper.
Coins: 3, Potions: 0, Buys: 1
EnvoyChapel buys Silver.
EnvoyChapel draws 5 cards: [Chapel, Estate, Copper, Envoy, Copper].
== EnvoyChapel's turn 4 ==
EnvoyChapel plays Envoy.
(EnvoyChapel shuffles.)
EnvoyChapel draws [Copper, Copper, Copper, Estate, Estate].
BigEnvoy chooses for EnvoyChapel to discard Copper.
EnvoyChapel plays Copper.
EnvoyChapel plays Copper.
EnvoyChapel plays Copper.
EnvoyChapel plays Copper.
Coins: 4, Potions: 0, Buys: 1
EnvoyChapel buys Silver.
(EnvoyChapel shuffles.)
EnvoyChapel draws 5 cards: [Copper, Silver, Copper, Copper, Envoy].
It has a terminal collision on turn 4 and instead of using the Chapel to trash the junk it plays the Envoy. I think in early game you're better off trashing your Coppers and Estates as quickly as possible. Plus this would avoid triggering the reshuffle and missing both your actions on the next run through the deck (though the simulators are bad at triggering reshuffles correctly anyways).
This is the code I used for EnvoyChapel.
{
name: 'EnvoyChapel'
requires: ['Envoy', 'Chapel']
gainPriority: (state, my) -> [
"Colony" if my.countInDeck("Platinum") > 0
"Province" if state.countInSupply("Colony") <= 6 \
or state.countInSupply("Province") <= 6
"Duchy" if 0 < state.gainsToEndGame() <= 5
"Estate" if 0 < state.gainsToEndGame() <= 2
"Platinum"
"Gold"
"Envoy" if my.countInDeck("Envoy") < 1
"Chapel" if my.countInDeck("Chapel") < 1
"Silver"
"Copper" if state.gainsToEndGame() <= 3
]
}
It's possible that even though it's playing incorrectly it may still not be as good as EnvoyBigMoney, but that's completely up in the air.