To answer my own question: yes, that approch is working correctly.
Here's a screen of a test project
* designed on Windows @120ppi
* then copied to my Linux box
* built with /usr/bin/lazbuild (without touching the form properties)
* then running on Linux @96ppi.
Observations:
* DesignTimePPI is 120, and never changes at runtime
* PixelsPerInch was 120 at designtime, changes at runtime to the value of Screen.PixelsPerInch
* FormCreate sets width and height to 500px (still in 120ppi as designed!)
* LCL scaling takes place (where exactly?)
* in FormShow, the width is now 400px (96ppi)
* In FormDestroy the form is not descaled as I suspected
* Instead, storing the width for the next session using ScaleFormToDesign(Width) scales it up from 400px to 500px
* Next time the app runs it sets the width again to 500px, and the scaling again decreases that to 400px
Just feels a bit wrong to store dimensions in the scaling the developer dictates, instead of the actual user scaling. But that's rather philosophic I suppose.