"Blender OBJ import puts Y-up rotation in object matrix, not vertex coords"
▸ SYMPTOM
After a headless Blender wm.obj_import(..., up_axis='Y'), per-vertex logic that reads raw .co.z as "vertical" silently uses the wrong axis. A Z-plane split computed on raw vertex coords keeps everything on one side — the "vertical" range it sees is tiny because it's actually reading the file's horizontal Z, not the true world-up Z.
▸ CAUSE
A headless Blender OBJ import with up_axis='Y' leaves the mesh's local vertex coords (bmesh.verts[i].co, mesh.vertices[i].co) in the file's Y-up frame. The Y→Z-up rotation is stored in the object's matrix_world, not baked into the vertices.
So obj.matrix_world @ v.co gives the true Z-up world coordinate, but raw .co.z reads the file's Z (a horizontal axis in the original coordinate space).
▸ FIX
Right after import, apply the object transform so mesh-local coords become world coords (Z-up):
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)After this, .co values are in the true Z-up world frame. The exporter's up_axis='Y' re-converts back to the file frame on the way out, so orientation still round-trips correctly.
Alternatively, transform verts manually: world_co = obj.matrix_world @ v.co — but applying the transform is simpler for scripts that do extensive per-vertex work.
▸ WHY IT WORKS
transform_apply bakes the object's matrix_world into the mesh data, multiplying every vertex by the rotation/scale/translation. After this, matrix_world becomes identity and the vertex coords are in world space, where Z is up — matching what per-vertex scripts expect.