The vendorName/clientName swap you opened with is the sharpest example in here, and it's worth calling out explicitly: strict schema enforcement wouldn't have caught it either. Both fields are valid strings in the right slot, so json_schema with strict:true passes the exact same way json_object mode did. Structural validity and correctness are different guarantees. The only way to catch the swap is a semantic check: a handful of invoices with known field values, asserted after extraction, rerun whenever the prompt or model changes.
The vendorName/clientName swap you opened with is the sharpest example in here, and it's worth calling out explicitly: strict schema enforcement wouldn't have caught it either. Both fields are valid strings in the right slot, so json_schema with strict:true passes the exact same way json_object mode did. Structural validity and correctness are different guarantees. The only way to catch the swap is a semantic check: a handful of invoices with known field values, asserted after extraction, rerun whenever the prompt or model changes.