A job constructed with a model, serialised, and executed against a row that had changed in the intervening four minutes.
// what it was
new SendInvoice($order); // serialises an id and
// re-fetches on wake
// which is right, and the problem was the opposite:
new ApplyDiscount($order, $order->total);
// the total was captured at dispatch and applied later,
// after two lines had been added.
Framework model serialisation re-fetches by identifier, which is the correct default and is why the order itself was fine. The captured total was a plain value, correct at dispatch and stale at execution — and the rule that came out of it is that a job takes identifiers and re-derives everything else, or takes a value and states in its name that the value is a snapshot.