The value is assigned to @aa at execution time.
For a SQL batch, at compilation time the optimizer has no idea what values will be assigned to @aa during execution so it has to make a guess.
Yes, we can tell from this particular example that @aa is assigned a static value so you could say the optimizer is a bit 'stupid', but then what would the optimizer do if any of the following were the case:
@aa = user_name()
@aa = getdate()
@aa = 'my name is '+use_name()
@aa = substring(@ab,1,5)+convert(varchar,getdate(),10)
The optimizer would have to be modified to be able to not only parse but intelligently tell if a @variable assignment is a truly static value (eg, 'VRVF ') won't change between compile time and run time.
----------------------
Because of the way the optimizer treats static variable assignments during the parsing of SQL batches, it's always a good idea to replace the variable references with their actual static values thus allowing the optimizer to compile the (SQL batch) queries with actual values (as with your original, fast-running version of the query).