For an OTA update to work the native code in the user’s app has to be the same as the native code in the new version of the app.
In the managed workflow this means the Expo SDK versions have to match.
In the bare workflow I believe you have to be more careful. e.g. imagine you add some native code to your project and change the JS code to call into that native code, then if you publish the new JS code.
If a user’s app downloads the new JS code it will not have the corresponding native code, so it will crash.
I have not used OTA updates in a bare workflow app, so I’m not sure if there’s some way that Expo detects this situation and prevents the old app from downloading the OTA update (other than for checking the SDK version.)
For a flowchart showing how it works for managed workflow apps see this:
So basically the published SDK 34 app will not see the OTA update for the SDK 40 app (because the SDK is a different version). But if you want more control over this, consider using release channels.