Most of this is @ericw
I suggested including texturemins as well as extents, which may have gotten lost in the noise. That would bring a struct to an equally odd 17 bytes (assuming short). It's hard to see where one byte could be saved (or what else could be used to usefully pad the struct) since everything else is either trivially calculated, dependent on run-time stuff, or not relevant to lighting. The only thing I can reasonably think of is a 3-byte compressed normal, but that can also be taken from surf->plane (and flipped for planeback), and is something that most people won't even use.
Either way, it's looking more like "accept the implementation annoyance on technical grounds" is going to be the lesser evil.
Anyway, the reason for also including texturemins is that it's value is dependent on the same "dot (v, s->texinfo->vecs[j]) + s->texinfo->vecs[j][3]" as produces weird results with extents. I'm not sure if this is even a problem with texturemins, or if it's only extents that need to be corrected. That aside, including texturemins would allow an implementation to skip over CalcSurfaceExtents entirely, which may give a slightly faster map load (something I think is worth trading a little extra disk space for). My research engine also calculates surface bounds in CalcSurfaceExtents, but I can push that to polygon building time just as easy.
My other comments about extents in my previous post were an implementation fault on my part, so you can ignore them.
Bug: offsets needs to be int, not unsigned int, because -1 is a valid offset (indicating that the surf doesn't have any lightmaps).
I've been following the func_msgboard discussion, but since most of it is mapping oriented I haven't really participated.