Some amazing news... The idle/stalling issue has been resolved!
I had a new theory about what was causing the problem and was now suspecting the fuel injectors were leaking or blocked, so I decided to let the car heat up and start stalling and then run a garden hose over the fuel rail and injectors to see if the problem improved. It didn't.
So i moved on to cooling other components including the throttle body and various sensors that I could reach on top of and around the engine but again, no change.
I was giving up but thought I might try cooling the crankshaft sensor (Volvo call it the RPM sensor) if I could find it.
I've never really suspected the camshaft or crankshaft sensors because ECUs can diagnose faults with these, and Volvo's documentation specifically says this ECU will. It should be simple: any discrepancy in readings between cam and crank sensors should be easy to detect and trigger a fault code.
But when I ran the hose over the sensor for a few seconds the car started idling normally. I let it heat up again and the idle problem resumed and I ran the hose over it again and the problem went away. I repeated this cycle a few times and it seemed pretty conclusive.
I pulled off enough parts to gain access to the sensor and could see that someone had been there before as the plug was broken and cable-tied together. Suspecting it might be a wiring/connection issue I tidied that up and gave it another go but the problem remained.
So I picked up a second-hand sensor (P/N 301713485 but looks to have since been superseded by 31331765) because it was quicker than ordering a new sensor (ok, and cheaper) and I have heard of no other failures of these in all of my research of similar problems. Today I fitted it and took it for a test drive and am very happy and relieved to report that the idle is good and there was no stalling.
All I need to do now is replace the timing belt, engine mounts, CV boots, fix the air conditioning, replace the leaking steering rack, get a wheel alignment, and fix whatever else breaks next... :)