Finally got to the bottom of this issue. Henrik spotted it.
There's a call to the sizeStorer to tell it that the collection is dirty and must be flushed. The new code doesn't call the flush in onRollback, resulting in exactly what we're seeing where the numRecords is 1 greater than the actual documents. The old code would run the sizeStorer call in the onRollback handler in a very fragile non-obvious manner, via a recursive call into the _increaseDataSize function with a opCtx==nullptr. Additionally, the old _changeNumRecords() function didn't call into the sizeStorer, so the code was entirely dependent on the order of calling first _changeNumRecords() immediately followed by _increaseDataSize().