One thing to keep in mind is that while `ob->actcol` is one-based (if
materials are assigned), `active_material_index` is zero-based so we
have a couple of places to make sure whenever materials are assigned,
we should always be in the range of `1..totcol` for `ob->actcol`.
In the particular case of the report, the last remaining material slot
was removed (`ob->actcol` gets rightfully zero then), but assigning a
material from the `template_ID` then calls `BKE_object_material_assign`
with zero as the active index.
The internal `act` number was corrected [ `std::max<int>(act, 1)` ],
but did not end up in `ob->actcol`.
`BKE_object_material_resize` [which all code assigning/removing/...
materials eventually comes down to] used to do the "sanitizing" actually
(but exited early when `totcol` didn't actually change).
There were other places to, clamping to a proper upper or lower bound.
So to resolve, move the code for sanitizing into its own function and
use it after `BKE_object_material_resize` (and from a couple of places).
Ref !143196