0008. A live module's namespace and interfaceId are frozen
- Status: Accepted
- Date: stated as the 0.x versioning policy in #273 and in AGENTS.md by #274 (both 2026-10-08); recorded 2026-10-09
Context
Lattice is pre-1.0 and may break its ABI and storage layouts when that improves correctness, which
fresh deployments absorb. A diamond already running a module cannot absorb it: a reordered or retyped
field silently corrupts its state, and a changed interfaceId breaks every integrator that checks
supportsInterface. Modules are live in places Lattice does not control, such as downstream projects
that pin a Lattice tag or submodule, so "released by Lattice" is too narrow a test.
Options considered
- Freeze once live anywhere. Live means a release deployment, a Lattice demo, or a known downstream deployment, on any network.
- Freeze at 1.0. Leaves running testnet and downstream diamonds exposed to breaking upgrades until then.
- Freeze at release tag. Ignores demos and downstream deployments of untagged code, and freezes modules nobody has deployed.
Decision
Once a module is live on any network:
- its ERC-7201 namespace is frozen, and its storage struct may only grow by appending fields at the end;
- its interfaceId is frozen, so its selector set stays as it is;
- a new selector goes in a separate interface with its own ERC-165 registration and map slot (0003);
- new state goes in a new namespace or in fields appended to the struct.
Before a module is live, its layout and interface may change freely for a fresh deployment: mark the
change ! with a BREAKING CHANGE: footer, say that a fresh deployment is required, and regenerate the
storage baseline. A patch release never changes a storage layout, a selector set or interfaceId, or the
LatticeRegistry/LatticeFactory bytecode.
Consequences
- A fix to a live module can grow its surface but never reshape it.
IStrategyManagerRecoveryexists so thatIStrategyManagerkeeps0xcce4011b, andIVaultCoreRecoveryso thatIVaultCorekeeps0xa86d8962; the vault-side latch lives in a new namespace,lattice.storage.VaultCoreRecovery, and the strategy manager's latch is a field appended toStrategyManagerStorage. - A diamond keeps the facets it was cut with. A fix reaches it only through a
diamondCut, so any new state must start valid from the zero value its existing storage holds. - Deciding whether a module is live needs knowledge of downstream deployments, which no check has.
Confirmation
make storage-checkcompares every annotated struct with the committed baseline. With--baseline-ref(CI passes the pull request's base) it also requires each struct to be an append-only extension of the layout at that commit. A reviewed fresh-deployment exception must be listed inscript/upgrades/storage-layout.resets.- No check freezes interfaceIds. An id change does change the interface's ERC-165 map slot, so
StorageSlotVerificationTestand the module's ERC-165 test fail until the constant is re-pinned, which puts the change in front of a reviewer.
References
- README: versioning and compatibility
- AGENTS.md: development stage and Solidity architecture and storage
- Storage-safety Action
IStrategyManagerRecovery.sol,IVaultCoreRecovery.sol