Just a followup, the second part actually worked. It was misleading because I had some outputs that werent supposed to be checked. When a read occurs on a transition from 1->0 or 0->1, it reads the variable BEFORE the assignment, so I can use that to my advantage in the assertion.
My final assertion was:
property verify_tms_negedge;
logic data_val;
@(posedge jtag_tck) disable iff(~rstn) (jtag_tms_oe,data_val = jtag_tms) |->
@(negedge jtag_tck) (jtag_tms == data_val);
assert_tms_negedge : assert property(verify_tms_negedge) else $error(“failed assertion verify_tms_negedge”);
(It also required a bit of RTL modification in the DUT).