In effect, there are two issues: Constraints would have caught the problems, but would not have solved them because the top-level P&R is just routing together macros and is not doing synthesis; it cannot, for example, add delays between blocks on the top level to solve hold violations.
It reminds me to add that it's an open question as to whether we have sufficient constraints for the boundary between the user project and the rest of caraval, or whether it's even possible to define such constraints in a universal way. Can we guarantee the timing of the wishbone clock and data once it crosses over into the user project?