Turns out the LRM does explain the exact same example as the one I was trying.
LRM 16.4 :
As with all immediate assertions, a deferred assertion’s expression is evaluated at the time the deferred assertion statement is processed.
However, in order to facilitate glitch avoidance, the reporting or action blocks are scheduled at a later point in the current time step.
The pass and fail statements in a deferred assertion’s action_block, if present, shall each consist of a single subroutine call.
The subroutine can be a task, task method, void function, void function method, or system task.
Hence system task like $display as part of action block executes in reactive region.
LRM 16.4.1 :
When a deferred assertion passes or fails, the action block is not executed immediately. Instead, the action block subroutine call (or $error, if an assert or assume fails and no action_block is present) and the current values of its input arguments are placed in a deferred assertion report queue associated with the currently executing process.
Such a call is said to be a pending assertion report.
In the Observed region of each simulation time step, each pending observed deferred assertion report that has not been flushed from its queue shall mature, or be confirmed for reporting. Once a report matures, it may no longer be flushed.
Then the associated subroutine call (or $error, if the assertion fails and no action block is present) is executed in the Reactive region, and the pending assertion report is cleared from the appropriate process’s deferred assertion report queue.
LRM 16.4.2 :
assign not_a = !a;
always_comb begin : b1
a1: assert (not_a != a);
a2: assert #0 (not_a != a); // Should pass once values have settled
end
When a changes, a simulator could evaluate assertions a1 and a2 twice—once for the change in a and once for the change in not_a after the evaluation of the continuous assignment.
A failure could thus be reported during the first execution of a1.
The failure during the first execution of a2 will be scheduled on the process’s deferred assertion report queue.
When not_a changes, the deferred assertion queue is flushed due to the activation of b1, so no failure of a2 will be reported.
Therefore the answer my own question at the top
how does the deferred assertion evaluate it’s expression?
[Ans] As with all immediate assertions, a deferred assertion’s expression is evaluated at the time the deferred assertion statement is processed.
However due to the concept of ‘deferred assertion flush point’ the glitches encountered during ‘simple immediate assertions’ are avoided using ‘deferred assertion’
NOTE : In general,observed deferred assertions prevent glitches due to order of procedural execution, but do not prevent glitches caused by execution loops between regions that the assignments from the Reactive region may cause.