Thanks Matthew for your pointers.
Since originally posting, I managed to simplify the problem by terminating the compilation server at the end of a build, which allows to introduce the assumption that the code doesn't change during the lifetime of the server.
Now, I'm observing that sometimes different compilation requests place the same package databases at different paths using the -package-db flags. From the point of view of GHC, it is as if the package databases had been moved from one location to another. In newer requests, GHC still looks for the interface files at the old locations, and fails when it doesn't find them.
Another difference between requests is that, even for a same package database, different interface files are present, depending on what the module under compilation imports transitively. This is causing failures sometimes but not always, I still need to pin exactly the circumstances. The error manifests as an attempt to load a missing interface file that is apparently not transitively needed.
If I understand correctly, all the packages pointed with -package-id and -package-db end up in the EPS. And this means that we can't expect to update the locations of the interface files without discarding and repopulating the EPS, correct? I'm thinking of this as approximately as costly as restarting the compilation server.
I can reasonably ensure that package databases aren't moved around between compilation requests. But from the standpoint of the build system, it would require some compromises to demand that all of the interface files of a package be available even when not all of them are transitively imported. Can we hope to have GHC cope with this dynamic membership of modules to Haskell packages during the build? Is this an ability that 8.10.7 already has?
Thanks,
Facundo