Digging into this more, I see that the exec dbo.sp_MSmaximum_cleanup_seqno call isn't returning a @max_cleanup_xact_seqno record.
This means the query
select top 1 @max_cleanup_xact_seqno = xact_seqno
from MSrepl_transactions with (nolock)
where
publisher_database_id = @publisher_database_id and
(xact_seqno < @min_xact_seqno
or @min_xact_seqno IS NULL) and
entry_time <= @min_cutoff_time
order by xact_seqno desc
returns nothing. I debugged it and it's doing a loop that populates the @min_xact_seqno variable with various values. Most would return a @max_cleanup_xact_seqno, but the last value placed in @min_xact_seqno isn't found at all in the msrepl_transactions table. So I'm wondering if this indicates an issue? Anyone know?