Release process
This document is not normative. It describes how a Makechain release is proposed, built, rehearsed, scheduled, and verified, how clients and operators stay compatible across one, how a node-local storage upgrade is carried out, and what assurance a release adds. It supports specification section 18, which fixes the release artifact, the schedule, rule dispatch, activation installers, and the emergency posture, and specification section 19, which lists the calibration items this process measures. Where this document and the specification differ, the specification governs.
1. The eight steps
Every release, however small, walks the same eight steps. Nothing is skipped for a small change; the steps are cheap for small changes and load-bearing for large ones.
1.1 Propose
A MIP names the change, its class, its consensus_critical surfaces, its governed parameter deltas,
and its rollout notes. The MIP's status field is a process label and never an activation oracle. The
finalized schedule is the only source of "is this live".
1.2 Classify
Two checks apply to the class and the consensus_critical flag, and they run in different places.
The structural check reads the descriptor's own manifest: it confirms that the declared class
admits every declared surface, that consensus_critical is set exactly when a consensus-critical
surface is declared, and that the ruleset identity conditions hold. This is specification R-18.3;
every validator runs it, it is the whole of the class check inside SCHEDULE_RELEASE validity, and
a descriptor it rejects cannot be scheduled. It needs no binary that can run the new ruleset.
The conformance-based check derives the class and the flag from the declared surfaces together with the conformance run of step 1.4, and rejects a descriptor that claims less than it changes. It runs in the governance tooling only, as a release-qualification gate, because deriving the class requires executing the corpus under the new ruleset. A release that fails it is not published, but its rejection is a process outcome and never a validity verdict; the two obligations it carries, that the shipped code matches its manifest and that a release stays inside its class, are the qualification gates specification section 18.2 names.
1.3 Implement
Implementation happens under a new ruleset_id behind rules_for, with the old branch retained.
The compiler enforces coverage: rules_for and every per-ruleset table are exhaustive matches with
no wildcard arm, so adding a ruleset fails the build until every surface has decided what it does.
1.4 Fix the formats
Conformance vectors are produced for both sides of activation: every canonical type, root, and proof under the old ruleset at heights before activation, and under the new ruleset at and after it.
- A format-preserving or versioned-state release reproduces the old vectors byte for byte.
- Regenerating a vector is an explicit format approval, carrying a
breaking-formatlabel on the change, and never a way to make a test pass. - Every commonware pin bump inside the release re-runs the ALPHA parity set.
1.5 Rehearse on devnet, twice
The devnet chainspec's genesis release is always the newest ruleset, so ordinary devnet work runs on the full rule set from height zero.
Separately, a transition devnet starts on the predecessor ruleset, schedules the release through
SCHEDULE_RELEASE, crosses the activation height under load with a validator restart and a
state-syncing reader inside the window, and checks the post-activation list of step 8. A transition
for any consensus-critical surface uses DynamicReshareV1, because the fixed profile cannot
schedule one. The deterministic simulation gate runs the same crossing.
1.6 Rehearse on testnet
The release is scheduled with testnet_lead_blocks of notice and observed for at least
post_activation_soak_blocks under real traffic. A defect found here is fixed by a new descriptor
and never by editing the scheduled one.
1.7 Schedule on mainnet
The descriptor, the release notes, and the client version that supports the release are published
together. Operators upgrade during the delay while the node reports the pending release. DEFER is
the tool when readiness lags, and is the reason a pending release is cheap to move while an
activated one is not.
1.8 Activate and verify
At the activation height:
- every validator re-derives every header field (INV-CONS-02);
- roots of pre-existing data are confirmed unchanged for every non-reset class;
- the exporter confirms replay parity with the live chain;
- the wasm verifier vectors pass against live proofs;
- the query tier projects the activation block, so the new release is readable from
blocks; - the release-activation system event is emitted.
The MIP is then closed with the activation heights recorded.
2. Qualifying a consensus-critical surface
A consensus-critical release activates at the first block of an epoch, after the handoff at the
boundary: the boundary is the final block of the preceding epoch, the old engine stops there, and
the new configuration runs from the first block of the activating epoch. activation_height is that
first block, never the boundary block.
Each consensus-critical surface has its own engine handoff, and a surface whose handoff is not qualified cannot be scheduled on a continuing chain. Qualification is the transition devnet of step 1.5, run with a validator restart across the boundary into the first activating block and a marshal catch-up across it. No surface is claimed qualified by the current draft.
3. Compatibility practice
3.1 Peers
The handshake fields and the peer refusal and warning rules are normative and are in specification section 18.8.
3.2 Clients and gateways
GetReleaseSchedule (REST GET /v1/releases) returns every descriptor, its activation height,
whether it is active, and the serving node's supported range. It is conformance-tested against the
header's active_release_digest so that it cannot drift. Clients that submit commands read the
schedule and refuse to build a command under a ruleset the node will not apply at inclusion. For the
rest, admission should answer UnsupportedRelease, which specification R-18.51 states as a
recommendation: it is local policy that never changes validity, so a node that admits such a command
anyway is not wrong, and the command simply fails at execution. RPC response shapes are additive
across releases, and a removal is a release note with one release of overlap.
3.3 Query tier and events
No history table carries ruleset_id as a column. Every one of them keys on the height, and the
blocks table carries the header fields, active_release_digest among them, so the release in
force for a row is read by joining its height to that table. A consumer that needs to segment by
release does it that way rather than expecting a per-row tag. The event schema version bumps only in releases, and the release-activation
system event is the one consumers key rebuilds on.
3.4 The proof service and the exporter
The proof service serves membership and ancestry proofs for any height under the ruleset of that
height, and the exporter serves current-state proofs at its live head under the active ruleset. A
verifier older than that ruleset receives UnsupportedRelease rather than a decoded absence. The
rule is in the proofs chapter of the specification.
3.5 Operator reporting
A node reports the pending release, its activation height, and whether this binary supports it, from the moment the release is scheduled, so that operators have the whole activation delay to upgrade. A node that has stopped on a validity disagreement is likewise visible through readiness. Both surfaces are specified in operations.
4. The client schedule crate
A dependency-light releases module in makechain-protocol holds:
- the schedule evaluator, which is the fold over activate, defer, cancel, and emergency entries;
- the rule identities;
- a packaged per-network snapshot of the finalized schedule.
It has no dependency on the state or consensus crates, so SDKs, gateways, the query tier, and the wasm verifier can all use it.
The chain is the runtime authority. A client refreshes the snapshot from an authenticated schedule
checkpoint, obtained through GetReleaseSchedule with its proof, before relying on an activation
height, because a DEFER, a CANCEL, or an emergency entry can postdate any packaged table.
A conformance test pins the packaged snapshot and the evaluator to the chain's fixtures at the snapshot height. It tests the evaluator, not permanent equality with a live schedule.
The wasm verifier is one artifact that verifies proofs under every ruleset. Proof formats are
versioned by ruleset_id, and UnsupportedRelease is the answer for a ruleset newer than the
artifact.
5. Node-local storage upgrades
A node-local storage upgrade is not a release. It is what happens when a client version changes how it lays out data on disk while roots and encodings stay the same.
Procedure:
- Rebuild the local database on the new binary, either by state sync from peers or by replay from the store.
- Keep the previous directory until the rebuilt node is
Servingand root-matched. - Only then remove the previous directory.
No storage upgrade rewrites a live database in place.
5.1 The BlobLayout restrict-then-widen recipe
The commonware BlobLayout recipe applies whenever a runtime layout changes:
- The upgraded binary first opens storage restricted to the previous layout, so that the previous binary remains able to open the same data.
- The restriction is lifted only once the previous binary is no longer a rollback target.
BlobVersion is the application-owned axis for Makechain's own blob formats and follows the same
discipline.
6. Classifying a dependency bump in practice
Dependency bumps are classified by what they change, not by which crate they touch. The specification fixes the outcomes; this section is how the reading is done.
A bump of commonware-codec, storage::qmdb, storage::bmt, storage::merkle, simplex::types,
glue::{stateful,dkg}, or marshal::standard::Deferred is presumed to need a release until the
reading shows otherwise. The bump's release notes are read for commonware's own signals:
| Signal in the upstream notes | Reading |
|---|---|
| "no migration path" | an ALPHA break |
| "coordinated switch" | a consensus-critical switch |
The reading then lands on one of the four outcomes the specification fixes: a reset if any root
computation or canonical encoding changes; a node-local storage upgrade if only crash recovery or an
internal format changes; a consensus_critical format-preserving release if a consensus-critical
switch changes; an ordinary dependency change if the bump is confined to non-consensus crates.
7. Release assurance
These are the release-specific additions to the standing assurance layers in validation:
- the two-sided conformance vectors of step 1.4;
- a per-ruleset behaviour matrix that runs every rule the release touches under each ruleset it exists in, and asserts that the difference is exactly the declared one;
- an installer test that executes the activation installer twice and asserts one set of writes;
- the deterministic simulation gate for the activation crossing, with a mid-window restart and state sync;
- the schedule-table conformance test that pins the client crate to the chain;
- the peer-range handshake test.
8. Emergency practice
Before activation, CANCEL and DEFER are available up to the activation height, except for a
locked consensus_critical entry, which is committed two epochs early. The activation delay exists
so that this remains possible, and min_activation_delay_blocks is calibrated to hold a full
operator upgrade cycle plus the time to notice a bad release and cancel it.
After activation there is no rollback, and remediation is a successor release. Governed pause flags
give a code-free response within min_emergency_delay_blocks while the fix is built.
If validators diverge on a ruleset bug, detection is an operational duty rather than a protocol guarantee, and it triggers the incident policy in operations. Recovery is a reset with an identity replacement, never a coordinated re-execution of finalized history.