It’s now working after several more hours of fiddling. After I stumbled on the solution I found the reason why from @Braiden
_new: https://github.com/stm32duino/Arduino_Core_STM32_
_old: https://github.com/rogerclarkmelbourne/Arduino_STM32_
The old core supports the DFU (rogerclarkmelbourne/STM32duino-bootloader) bootloader I’m using. The newer core does not. Switching variants had the side effect of switching cores which is why my code started running.
By default PlatformIO selects ‘Arduino_Core_STM32’, it does not support dfu(usb). Adding ‘board_build.variant = BLUEPILL_F103XX’ to ‘platformio.ini’ causes PlatformIO to use the other core ‘Arduino_STM32’. This one does support dfu and everthing works fine.
PlatformIO should not give you the option of using ‘dfu’ for uploading with the blue pill and the ‘Arduino_Core_STM32’ which it defaults to, since it can’t possibly work.