one must not be able to mix packages from 3.3.1 commit XYZ with ones from 3.3.1 commit ABC), but the general way how packages work stays.
technically it is not necessarily always so
unless SemVer is enforced, what is absolutely needed is type compaticility (or actually ABI compatibility for all what ABI includes).
as long as ABI is binary the same - it is responsibility for programmer to fo it or nor (think of commercial software updates, updating one DLL/BPL while retaining others)
If updated function/class/unit was not used, then it seems to be plausible scenarion to still load packages
granteed, different Lazarus versions would probably have different ABIs somewhere in the RTL code, which would then "by dependencies tree" make everything else incompatible too.
so it seems that formally correct approach would be to have some nested hash, like TigerTree was p2p programs, and then use it to sign all the exported things. Or like today fashionable blockchains
including all the name mangling, compiler switches, architcture and what not
and including the hashes of the interfaces they depend on
i tried to make a very simplified image of data structures required and code points when they would be checked and... frankly, failed.
this OTOH seems to be intimately connected to debug inoformation generations. The amount of formalized specs that describe ABI with details enough to "sign" it for compativility sake - should also be enough for debugger to operate Evaluate, Watch, Call Stack, etc
Like, if there is no comprehensive information on interface types, then neither debugger can introspect, not BPL dnamic loader can do runtime type safety checks
Plus it is complicated by unit initializatoin/finalizations made in a mixed environment of BPLs statically and dynamically loaded.
I met bugs in XE2 Delphi, rather obscure ones. Deleting of const values in some edge cases.
It all sounds pretty easy before you start thinking, then it is snowballing
----
Granted, with Delphi/FPC speed and today internet bandwidth and disk sizes - the stupid "just recompile them all" solution is practical enough, even if annoying