Thank you for the response!
Yup, if I set lstrip=False, I see the same behavior:
tokenizer('This hamburger tastes great') 
>> [2, 15, 30001, 53, 8, 345,3]

tokenizer.decode([2, 15, 30001, 53, 8, 345,3]) 
>> ('Thishamburger tastes great')

Digging through the code, my hypothesis is:

The pre-tokenizer replaces the meta-symbol with a space character, when applied to raw text.
The decoder reverses the effects of the pre-tokenizer.
When the decoder sees a token with a meta-symbol; it inserts a space (for correct viewing)
Since the AddedTokens don’t go through the same pipeline (i.e. no metasymbol is added); I’m not sure if the pre-tokenizer is applied / if decoding works as expected.

Any thoughts on what could be going wrong? Or how one might approach this?