DevelopersSeptember 14, 2026by
EmpoorioChain Core
EmpoorioChain Core

Engineering Notes #7: post_upgrade Checks the Migration, Not the World

Background

A Substrate runtime upgrade with a storage migration has three hooks: pre_upgrade captures state, on_runtime_upgrade migrates, post_upgrade verifies. try-runtime runs them against a snapshot of real state before the upgrade is proposed.

What was found

During the September 2026 upgrade sequence, four post_upgrade hooks in EmpoorioChain's runtime were written as assertions about the world: this storage item is empty, this value is zero, no node is registered. They did not check what their own migration had done. Two failure modes follow:

  1. On a chain where the world is different — a testnet with accumulated state — the hook fails for reasons unrelated to the migration, blocking a correct upgrade.
  2. If the migration silently does nothing (wrong storage prefix, wrong version guard), a hook that checks the world rather than the effect passes anyway.

The rule

post_upgrade verifies the effect of its own migration and nothing else: compare against what pre_upgrade captured; assert that the old key is gone and the new key holds the transformed value; assert the storage version was bumped. It does not assert emptiness, zero, or absence of unrelated things.

All four hooks were rewritten to this form.

Why it belongs in a series about measurement

Because it is the same failure as a validator audit that cannot fail or a compliance check that is tautological: a verification that does not depend on the thing it claims to verify. The question the ecosystem now asks of every check — what would have to be true for this to fail? — applies to runtime hooks as much as to scripts.

try-runtime remains mandatory before every migration, and it is what exposed these hooks: run against the live snapshot, the world-asserting hooks failed on state the migration had not touched.

From the runtime 213–220 upgrade sequence, September 2026.

Share this article