To Inherit Version1 to Version2, Or to Copy Code + Inheritance Order from Version1 to Version2?

That's correct, my plan was to not have any storage variables in the implementation itself. ALL storage would live in the StorageV1 style contracts. That way there we don't have to think about __gaps. Leave that to the geniuses at OpenZeppelin :-).

I think I found a possible solution to this which leans mainly on the Compound code-copy approach and abstract contracts to keep inheritance ordered.

// Inherit all things onto an abstract contract, and add whatever storage vars you need after that
abstract contract OpenQStorageV0 is
    OwnableUpgradeable,
    UUPSUpgradeable,
    ReentrancyGuardUpgradeable,
    Oraclize
{
    BountyFactory public bountyFactory;
    OpenQTokenWhitelist public openQTokenWhitelist;
}

// Inherit the abstract contract onto your main implementation
// This works, as OpenQV0 overrides any necessary methods from the base contracts
contract OpenQV0 is OpenQStorageV0 {...}

// ----- MANY YEARS AND HACKS LATER -----

// Later, there's a New Base Contract you want to inherit
contract NewBaseContract {
    uint256 public foo;

    function setFoo(uint256 _foo) public {
        foo = _foo;
    }
}

// We Inherit the old storage contract, THEN we inherit from NewBaseContract, THEN we add any new custom storage variables
// I believe this maintains the storage order from before, and adds new ones after them.
// UNLESS there are dependency interactions between NewBaseContract and the old ones resulting from C3 linearization?
abstract contract OpenQStorageV1 is OpenQStorageV0, NewBaseContract {
    uint256 public newStorageVar = 456;
}

// Inherit the newer abstract contract onto your main implementation
contract OpenQV1 is OpenQStorageV1 {...}

I just tried this upgrade approach using code copy + abstract contracts for storage, and it worked without messing up storage layout :cowboy_hat_face:. I could update foo and newStorageVar.

So after this sequence of inheritance (NEVER putting storage vars directly in the implementation contract), I believe the storage layout of OpenQV1 would look something like this after upgrade:

[OpenZeppelin upgradable inherited contracts]
[2 OpenQStorageV0 storage variables]

after upgrade append....

[1 foo from NewBaseContract]
[1 newStorageVar from OpenQStorageV1]

Then I'd continue this chaining into the bright and storied future of the dApp...

Thoughts? Is this awful? What gotchas am I missing, or will this let me safely add both NEW CUSTOM STORAGE VARIABLES and NEW 3rd PARTY BASE CONTRACTS?