OK, figured it out. I used
'CrossoverSlopes', 12,...
which is wrong for my use case where I have two 2nd order Butterworth filters cascaded.
Cascading any order Butterworth filter produces 2x that order Linkwitz-Riley. So, cascading two 2nd order Butterworth filters creates a LR-4 design. This means the crossover slope is 24 dB/octave, not 12 as I thought.
Changing the above line to
'CrossoverSlopes', 24,
makes the output from crossoverFilter() the same as my "manual" setup using butter() and filter().