I dug into this a little more and the uninitialized RAM issue isn't quite what I thought. For attributes, you set some of the attributes, but not all of them, so parts of the title screen and especially the playfield can be wrong depending on power-on RAM state. You can make the attributes a lot easier to write by copying 64 bytes in a loop, rather than copying hardcoded bytes one by one, which is a lot harder to maintain.
For palettes, you actually do set them, just too early. The PPU ignores most writes for the first 30,000 cycles or so after powering on, and your palette writes happen during that PPU initialization window. You can test this in Mesen with the "Clear PPU $2000/1/5/6/7 throughout first frame after reset/power-on" feature. Most emulators don't emulate PPU init at all.